先说结论
我们可以顺序应用下面四条规则来判断this的绑定对象:
- 如果是由
new
调用,绑定到新创建的对象。 - 如果是由
call
或者apply
、bind
调用,绑定到指定的对象上。 - 如果是由上下文对象调用(即this是对象中的方法里使用的),绑定到那个上下文对象上。
- 如果是默认情况下,严格模式绑定到undefined,否则绑定到全局对象上。
但是ES6中的箭头函数并不会使用者四条标准的绑定规则。箭头函数会继承外层函数调用的this绑定。
new
绑定
function foo(a){
this.a=a;
}
var bar=new foo(2);
console.log(bar.a); //2
我们使用new
来调用foo(…)时,会构造一个新对象并把它绑定到foo(…)调用中的this上。
显式绑定call
、apply
、bind
function foo(){
console.log(this.a);
}
var obj={
a:2;
};
foo.call(obj); //2
call
用来将函数foo中的this绑定到obj上。
隐式绑定(上下文对象调用)
如果this
所在的函数是对象中的方法的话,this就会指向这个对象。
function foo(){
console.log(this.a);
}
var obj={
a:2,
foo:foo
};
obj.foo(); //2
foo
此时是obj对象的一个方法,所以this
就会指向obj。
但是对象属性引用链中只有上一层或者说最后一层在调用位置中起作用:
function foo(){
console.log(this.a);
}
var obj2={
a:42,
foo:foo
};
var obj1={
a:2,
obj2:obj2
};
obj1.obj2.foo(); //42
隐式丢失也是this
隐式绑定中常见的问题:
function foo(){
console.log(this.a);
}
var obj={
a:2,
foo:foo
};
var bar=obj.foo; //函数别名!
var a="oops, global"; //a是全局对象的属性
bar(); //“oops, global”
此时bar
是obj.foo
的一个引用,但实际上它引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定。
回调函数丢失this
绑定是也是非常常见的问题:
function foo(){
console.log(this.a);
}
function doFoo(fn){
fn(); //调用位置
}
var obj={
a:2,
foo:foo
};
var a="oops global"; //全局对象属性
doFoo(obj.foo); //"oops global"
函数doFoo
传入了函数作为参数,并在doFoo
中调用。我们可以把它看做将函数copy到了doFoo
函数体中,调用的时候this
指向的是全局作用域。
默认绑定
function foo(){
console.log(this.a);
}
var a=2;
foo(); //2
在这个例子中,函数调用应用了this的默认绑定,this指向全局对象。
如果使用严格模式(strict mode),则不能将全局对象用于默认绑定,this
会绑定到undefined
。
function foo(){
"use strict";
console.log(this.a);
}
var a=2;
foo(); //TypeError: this is undefined
箭头函数中的this
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。
function foo(){
return (a)=>{
console.log(this.a);
}
}
var obj1={
a:2
};
var obj2={
a:3
};
var bar=foo.call(obj1);
bar.call(obj2); //2,不是3!
foo()
内部创建的箭头函数会捕获调用时foo()
的this。由于foo()
的this
绑定到obj1
,所以bar(引用箭头函数)的this
也会绑定到obj1
,箭头函数的绑定无法被修改。
再看一个例子:
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 });
// id: 42
foo
调用时传入的是什么对象,this就指向谁。如果是普通函数,执行时this应该指向全局对象window,这时应该输出21(严格模式则输出undefined)。
参考书籍:《你不知道的JavaScript》
详情可见我的博客:http://delaprada.com