wj'blog


  • 首页

  • 分类

  • 归档

  • 标签

  • 关于

  • 搜索

target与currentTarget的区别

发表于 2018-03-10 | 分类于 JavaScript

介绍

在事件处理函数的event对象中,有一个target属性,我们都知道它是指事件目标。其实在event中还有一个currentTarget属性,不知道这个属性还好,知道的话可能会弄混与target的区别。

先看下定义(参考JS高级程序设计):

  • target:事件的目标
  • currentTarget:其事件处理程序当前正在处理事件的那个元素

看完这两个定义,或许还是似懂非懂的感觉,我一直觉得实践是最好的验证方法,所以来写个极其简单的例子吧。

实例

html文件,写一个简单的列表

1
2
3
4
5
<ul>
<li>111</li>
<li>222</li>
<li>333</li>
</ul>

js文件

1
2
3
4
5
6
7
var aUl = document.getElementsByTagName('ul')[0];

aUl.addEventListener('click',function(event){
console.log(this);
console.log('target: ',event.target);
console.log('currentTarget: ',event.currentTarget);
},false)

当看到这个例子,你或许就明白我要说啥了。运行程序,会发现,target指向你所点击的那个li元素,而currentTarget始终指向ul元素。

所以,我可以用自己的话再来定义一下这两个属性了:

  • target:指向触发事件的元素。上例中,是li触发的事件,冒泡到ul上执行的
  • currentTarget:指向添加事件的元素。很好懂啊,上例中,事件是绑定在ul上的

另外需要注意的是,this始终等于currentTarget,在上例中,始终指向ul,不信的话也可以试下啊,我们将事件绑定到第二个li上试试:

1
2
3
4
5
6
7
8
var aUl = document.getElementsByTagName('ul')[0];
var aLi = aUl.getElementsByTagName('li')[1];

aLi.addEventListener('click',function(event){
console.log(this);
console.log('target: ',event.target);
console.log('currentTarget: ',event.currentTarget);
},false)

结果就不用多说了。

js中对象的属性类型

发表于 2018-02-20 | 分类于 JavaScript

简介

在 javaScript 中,对象的属性分为两种类型:数据属性和访问器属性。

数据属性

它包含的是一个数据值的位置,在这可以对数据值进行读写。数据属性包含 4 个特性:

  • configurable:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或能否把属性修改为访问器属性,默认为 true
  • enumerable:表示能否通过 for-in 循环返回属性
  • writable:表示能否修改属性的值
  • value:包含该属性的数据值。默认为 undefined
阅读全文 »

JavaScript严格模式

发表于 2018-01-13 | 分类于 JavaScript

介绍

ECMAscript 5添加了“严格模式”,会使得Javascript在更严格的条件下运行,设立”严格模式”的目的,主要有以下几个:

  • 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
  • 消除代码运行的一些不安全之处,保证代码运行的安全;
  • 提高编译器效率,增加运行速度;
  • 为未来新版本的Javascript做好铺垫。

说明
此文参考了阮一峰的Javascript 严格模式详解,对于原文中的例子我都一一测试了,有个别测试结果是不一样的,后面已做说明。那些我也是比较迷惑的,如果有错误,希望指出。

阅读全文 »

数组去重(只讲两种方法)

发表于 2018-01-12 | 分类于 JavaScript

介绍

数组去重的方法不少,每个人都有自己喜欢用的方法,我只讲两种我喜欢用的方法。

方法一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Array.prototype.unique = function(){
var obj = {};
var arr = [];
var len = this.length;

for(var i=0;i<len;i++){
if(!obj[this[i]]){
obj[this[i]] = 'a';
arr.push(this[i]);
}
}

return arr;

}

此方法利用对象属性不能重复的性质,将数组值遍历到对象的属性上,如果属性名的属性值已存在,则忽略,如果属性值不存在,则添加新属性,并赋值(注意赋的值不能为假),然后将这个属性名push到新数组里,遍历结束,返回新数组。

方法二

1
2
3
Array.prototype.unique = function(){
return Array.from(new Set(this))
}

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

同时ES6为数组新增了Array.from方法,用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。

new Set可以将数组转化为Set数据结构,并去重,如:

1
2
3
new Set([1,2,3,3])

//{1, 2, 3}

再用Array.from将Set转回为数组

判断数组类型的方法

发表于 2018-01-11 | 分类于 JavaScript

介绍

我们都知道js中可以使用typeof来判断数据类型,但是有时它并不很精确,就拿数组来说,判断的结果是object。所以我们需要用其他方法来准确判断到底是不是一个数组。

方法

有四个方法,很简单:

instanceof

语法:object instanceof constructor
意思是说object的原型链上是否有constructor的prototype,object是检测的对象,constructor是一个构造函数,结果返回布尔值

1
2
var arr = [];
arr instanceof Array; //true

constructor

语法:object.constructor 返回object的构造函数

1
2
var arr = [];
arr.constructor == Array; //true

Object.prototype.toString.call()

1
2
var arr = [];
Object.prototype.toString.call(arr); //"[object Array]"

Array.isArray()

1
2
var arr = [];
Array.isArray(arr); //true

此方法可能有兼容问题

总结

综合上面提到的方法,写一个比较完善的方法:

1
2
3
4
5
6
7
8
9
10
11
function isArrayFn(v){
if(Array.isArray){
return Array.isArray(v)
}else{
return Object.prototype.toString.call(v) === '[object Array]';
}
}


var arr=[];
console.log(isArrayFn(arr)); //true

构造函数的继承

发表于 2018-01-10 | 分类于 JavaScript

什么是继承

简单来说,就是一个对象使用另一个对象的属性和方法,并且不要影响原对象的功能。

阅读全文 »

关于构造函数

发表于 2018-01-06 | 分类于 JavaScript

介绍

对于构造函数的使用很多人都很熟悉,但是对其原理很多也是比较模糊的,虽然这并不影响使用,但是熟悉了原理,才会少犯错。简单叙述下,算是给自己加深个印象。

构造函数的使用

想必都很熟悉

1
2
3
4
5
6
7
function Foo(name, age){
this.name = name;
this.age = age;
}

var foo = new Foo('xiaoming', 15)
// {name: "xiaoming", age: 15}

构造函数名的首字母是大写,但却并不是非得是大写,只是一种惯例,为了区别其他函数,构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。所以即使你小写也不会有问题,但是肯定不建议这么做,提出来也只是为了让你注意不要小写首字母。

构造函数也是函数,只不过它可以用来创建对象。不过必须使用new操作符,使用new创建实例会经历4个步骤:

  1. 创建一个新对象
  2. 将构造函数的作用域赋给新对象(this指向新对象)
  3. 执行构造函数中的代码(this赋值)
  4. 返回新对象

总结:

  • 函数名首字母大写
  • 没有显示创建对象
  • 直接将属性和方法赋给了this对象
  • 没有return 语句(默认存在)

内部原理

根据上面的4个步骤,不难知道构造函数的内部原理,每当声明一个构造函数时:

  1. 在函数体最前面隐式的加上this = {}
  2. 执行this.xxx = xxx
  3. 隐式地返回this

如:

1
2
3
4
5
6
function Foo(name, age){
var this = {}; //隐式加上
this.name = name;
this.age = age;
return this; //隐式返回
}

为了验证是否正确,我们可以自己实现一个构造函数,不过不是使用this,而是使用that:

1
2
3
4
5
6
7
8
9
function Foo(name, age){
var that = {};
that.name = name;
that.age = age;
return that;
}

var foo = Foo('xiaoming', 15);
console.log(foo.name); //'xiaoming'

注意,这时我们不需要使用new操作符了,因为所有需要的步骤我们都已经添加了,这就是一个普通的函数,返回一个对象。

关于返回值

我们都知道,构造函数默认返回了this,那么假使我们自己不小心添加了return语句会怎么样呢。不妨试一下:

1
2
3
4
5
6
7
8
function Foo(name, age){
this.name = name;
this.age = age;
return this;
}

var foo = new Foo('xiaoming', 15);
console.log(foo); //{name: "xiaoming", age: 15}

显然,添加return this是不会有什么问题的。

现在我们返回一个普通值:

1
return 123;

如果你试了,会发现也不会影响结果,如果你再顺便试了一下返回字符串和布尔值,发现依旧不会影响结果。现在你知道我想说啥了,没错,当你试着返回一个对象时,结果就是那个对象了,继续试着返回数组和函数,也会影响结果,使得构造函数失去应有的作用。

总结

  • 构造函数默认返回了this
  • 如果手动返回了值类型,对结果没影响
  • 如果手动返回了引用类型,该构造函数失效

ES6函数和对象

发表于 2017-12-26 | 分类于 ES6

介绍

本次介绍ES6函数和对象的一些扩展,更多详情参考:函数的扩展,对象的扩展

函数

函数参数的默认值

直接为函数的参数指定默认值

1
2
3
4
5
6
function test(x,y='world'){
console.log(x,y);
}

test('hello'); //hello world
test('hello','hello'); //hello hello

参数变量是默认声明的,所以不能用let或const再次声明

1
2
3
4
function test(x,y='world'){
let y = 'world'; //error
console.log(x,y); //error
}

参数默认值的位置

1
2
3
4
5
6
7
8
9
10
function f(x = 1, y) {
return [x, y];
}

f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // 报错
f(undefined, 1) // [1, 1]

//带有默认值的参数应该放在末尾,这样才可以省略

作用域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。
//等到初始化结束,这个作用域就会消失。

var x = 1;

function f(x, y = x) {
console.log(y);
}

f(2) // 2
f() //undefined

//上面代码中,参数y的默认值等于变量x,在作用域里,x指向的是第一个参数x,而不是全局变量x。x没赋值时,y就是undefined。


//另一种情况
var x = 1;

function f(y = x) {
console.log(y);
}

f(2); //2
f(); //1

//这个作用域里面,变量x本身没有定义,所以指向外层的全局变量x

rest 参数

ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。

当你不确定参数个数时,你可以这样做

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var sum;
function add(...args){
for(var v of args){
sum+=v;
}

return sum;
}

add(1,2); //3
add(1,2,3); //6
add(1,2,3,4,5,6); //21

//add函数是一个求和函数,利用 rest 参数,可以向该函数传入任意数目的参数。
//这就是扩展运算符的使用,rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中

箭头函数

这个使用率很高的。
ES6 允许使用“箭头”(=>)定义函数

1
2
3
4
5
6
let fn = v => v*2;
//等同于
let fn = function(v){
return v*2;
}
console.log(fn(2)); //4

如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。

1
2
3
4
5
6
7
8
9
10
11
let fn = () => 2;
//等同于
let fn = function(){
return 2;
}

let fn = (num1,num2) => num1+num2
//等同于
let fn = function(num1,num2){
return num1+num2;
}

如果加上{},return不能省略的

1
2
3
4
5
6
7
8
9
10
11
//当不写{},且没有其他操作时,默认返回值
let fn = () => 2;
console.log(fn()) //2

//当加上{}时,return不可省略
let fn = () => {2};
console.log(fn()) //undefined

//{}里要写完整的代码
let fn = () => {return 2}
console.log(fn()) //2

如果箭头函数只有一行语句,且不需要返回值

1
let fn = () => alert(1)

this

尤其要注意箭头函数里的this指向,
函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}

var id = 21;

foo.call({ id: 42 }); // id: 42

//定时器里有个箭头函数,它的this指向的是定义时的对象,即foo。而foo的this指向了{id:42}这个对象,所以结果是42。


//现在换回ES5的写法
setTimeout(function(){
console.log('id:', this.id);
}, 100);

//此时this指向的是window对象,所以this.id指的就是全局变量id,为21。

另外要注意的:

  • 箭头函数不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

  • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

  • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

对象

属性的简洁表示法

ES6 允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//属性简写
let name = 'xiaoming';
let age = 20;

let obj = {
name,
age
}
//等同于
let obj = {
name:name,
age:age
}

//只要属性名和属性值变量同名即可



//方法简写
let obj = {
showName(){
alert('xiaoming');
}
}
//等同于
let obj = {
showName:function(){
alert('xiaoming');
}
}

属性名表达式

在ES5中,使用字面量方式定义对象(使用大括号),只能使用标识符定义属性名:

1
2
3
4
var obj = {
foo: true,
abc: 123
};

现在ES6允许字面量定义对象时,用表达式作为对象的属性名,即把表达式放在方括号内:

1
2
3
4
5
6
7
8
let key = 'num';

let obj = {
[key]:123,
['na' + 'me']:'jhon'
}

console.log(obj); // {num: 123, name: "jhon"}

表达式还可以用于定义方法名:

1
2
3
4
5
6
7
let obj1 = {
['h' + 'ello'](){
console.log('hi');
}
}

obj1.hello(); //hi

注意,属性名表达式如果是一个对象,默认情况下会自动将对象转为字符串[object Object]:

1
2
3
4
5
6
const keyA = {a:1};
let obj2 = {
[keyA]:'A'
}

console.log(obj2); //{[object Object]: "A"}

Object.assign

合并对象

Object.assign方法用于对象的合并,将源对象的所有可枚举属性,复制到目标对象。第一个参数是目标对象,后面的参数都是源对象。

1
2
3
4
5
6
7
8
9
let o1 = {'a':1};

let o2 = {'b':2};

let o3 = {'c':3};

Object.assign(o1,o2,o3);

console.log(o1); //{a: 1, b: 2, c: 3}

如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

1
2
3
4
5
6
7
8
9
10
//将上面例子改变一下
let o1 = {'a':1,'b':2};

let o2 = {'b':3,'c':4};

let o3 = {'c':5};

Object.assign(o1,o2,o3);

console.log(o1); //{a: 1, b: 3, c: 5}

拷贝

1
2
3
4
5
6
7
8
var obj1 = {
'a':1,
'b':2
}

var obj2 = Object.assign({},obj1);

console.log(obj2); //{a: 1, b: 2}

需要注意的是,Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。

1
2
3
4
5
6
7
8
9
10
11
var obj1 = {
'a':{
'b':1
}
}

var obj2 = Object.assign({},obj1);

obj1.a.b = 2;

console.log(obj2.a.b); //2

Linux环境安装mongodb

发表于 2017-12-23 | 分类于 服务器

介绍

上篇介绍了Linux环境下安装Node.js的步骤,紧接着来安装mongodb。
另外,推荐我的另一篇 Windows下图文详解Mongodb安装及配置,先在Windows下熟悉下mongodb,再来看这篇文章会更容易明白。

阅读全文 »

Linux环境部署Node.js

发表于 2017-12-23 | 分类于 服务器

介绍

先前在阿里云ECS上部署Node.js,碰到不少坑,都是自己不仔细造成的,所以准备再部署一遍,并记录下来。我将我的服务器重置了,这次选择的是CentOS 7.4 64位,上次的是7.2的。

使用到的工具:Xshell5 ,链接服务器;Xftp5,上传工具

安装步骤

你可以选择本地下载node.js,然后上传到服务器,也可以通过wget直接在服务器端下载。

阅读全文 »
1234…6
wungjyan

wungjyan

56 日志
9 分类
24 标签
RSS
练习册
© 2020 wungjyan
由 Hexo 强力驱动
|
主题 — NexT.Mist v5.1.2
本站访客数 人次 本站总访问量 次