this指向

This指向规则

This指向分为四种绑定规则:默认绑定、隐式绑定、显式绑定以及关键字new绑定。在ES6之后,又有了箭头函数中的this规则。

实例一

1
2
3
4
5
6
7
8
9
10
function foo() { 
console.log(this.bar);
}
var bar = "bar1";
var o2 = {bar: "bar2", foo: foo};
var o3 = {bar: "bar3", foo: foo};

foo();
o2.foo();
foo.call(o3);

三个函数调用分别输出:”bar1”, ”bar2”, ”bar3”。因此这三者分别是默认绑定、隐式绑定和显式绑定。

实例二

1
2
3
4
5
6
7
8
9
10
11
12
var name = 'Nicolas';
function Person(){
this.name = 'Smiley';
this.sayName=function(){
console.log(this);
console.log(this.name);
};
setTimeout(this.sayName, 0); // 第二次输出
}

var person = new Person();
person.sayName();

第一次输出的是Person, Smiley。第二次输出的结果是window,Nicolas。尽管setTimeout是在构造函数中定义的,但是调用的时候,是在window中调用。SetTimeout等许多之后被触发的事件当中,一定要注意this的指向,这是基于调用点(call stack)的 。

实例三

1
2
3
4
5
6
7
8
9
10
11
function Person() {
this.name = "Smiley";
this.sayName = function(){
console.log(this);
console.log(this.name);
};
}

let person = new Person();
let sayNameCopy = person.sayName;
sayNameCopy();

答案是window和undefined。因为,这个时候符合默认绑定的规则。

实例四

1
2
3
4
5
6
7
8
9
10
function Person() {
this.name = "Smiley";
this.sayName = ()=> {
console.log(this);
console.log(this.name);
};
}

let person = new Person();
person.sayName.call({name: "Nicolas"});

我们只改动了一处:把sayName改为箭头函数。结果则变成了Person和”Smiley”。这是因为箭头函数并没有自己的this,被定义在哪里,this就指向谁,且优先级比显式调用高,因此,this仍指向Person。

实例五

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Person(){
this.age = 0;
setTimeout(function () {
console.log(this.age); // 输出undefined
}, 1000);
}
var p = new Person();


function Person(){
this.age = 10;
setTimeout(()=> {
console.log(this.age); // 输出10
}, 1000);
}
var p = new Person();

在上面没有使用箭头函数的例子当中,setTimeout内部的函数是被global调用的,而global没有age这个属性,因此输出undefined。

第二个例子使用了箭头函数,this就会使用lexical scope中的this,就是Person,因此输出10。

绑定优先级

  1. 箭头函数
  2. 关键字new调
  3. 显式绑定
  4. 隐式绑定
  5. 默认绑定