传闻JavaScript有两座大山——this与作用域,在函数中,这两个难点会交织在一起。本来this就一脸懵逼,又有同样懵逼的作用域,所以说很多人就会
es6中,又出现了一个箭头函数,箭头函数的this又比普通函数的this“骨骼惊奇”得多。欲搞清楚箭头函数的this指向,必须要搞清楚this的四种绑定机制。
参考链接:深入理解this机制系列第一章
默认绑定
全局环境中,this默认绑定到window
在控制台中分别执行以下代码,注意,全局变量相当于window对象的属性
<1> console.log(this==window) // true
<2> var a={
foo:"123",
bar:"456",
that:this
}
a.that // window
<3> this // window
函数独立调用或者被嵌套函数独立调用时,this默认绑定到window
虽然test()函数被嵌套在obj.foo()函数中,但test()函数是独立调用,而不是方法调用。所以this默认绑定到window
var a = 0;
var obj = {
a : 2,
foo:function(){
function test(){
console.log(this.a);
}
test();
}
}
obj.foo();//0
立即执行函数实际上是函数声明后直接调用执行
var a = 0;
function foo(){
(function test(){
console.log(this.a);
})()
};
var obj = {
a : 2,
foo:foo
}
obj.foo();//0
闭包中被返回到外部的函数也是独立调用
var a = 0;
function foo(){
var a = 1
function test(){
console.log(this.a);
}
return test;
};
var obj = {
a : 2,
foo:foo
}
obj.foo()();//0
在这个例子中,如果test函数中console的是变量a,那么按照闭包的运行原理输出的是1,但这里输出的是this.a,this又绑定到了window,所以输出的值是0
隐式绑定
函数做为对象方法调用时(即方法调用),this隐式绑定到该直接对象
function foo(){
console.log(this.a);
};
var obj1 = {
a:1,
foo:foo,
obj2:{
a:2,
foo:foo
}
}
//foo()函数的直接对象是obj1,this隐式绑定到obj1
obj1.foo();//1
//foo()函数的直接对象是obj2,this隐式绑定到obj2
obj1.obj2.foo();//2
隐式丢失
隐式丢失是指被隐式绑定的函数丢失绑定对象,从而默认绑定到window,有以下几种情况会造成隐式丢失
<1>函数别名
var a = 0;
function foo(){
console.log(this.a);
};
var obj = {
a : 2,
foo:foo
}
//把obj.foo赋予别名bar,造成了隐式丢失
var bar = obj.foo;
bar();//0
<2>参数传递
var a = 0;
function foo(){
console.log(this.a);
};
function bar(fn){
fn();
}
var obj = {
a : 2,
foo:foo
}
//把obj.foo当作参数传递给bar函数时,有隐式的函数赋值fn=obj.foo
bar(obj.foo);//0
<3>作为内置函数的回调
var a = 0;
function foo(){
console.log(this.a);
};
var obj = {
a : 2,
foo:foo
}
//如果没有隐式丢失,this应该指向obj
setTimeout(obj.foo,100);//0
//结果为0,证明函数作为作为内置函数的回调时发生了隐式丢失
<4>其他特殊情况
function foo() {
console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
//将o.foo函数赋值给p.foo函数,然后立即执行。相当于仅仅是foo()函数的立即执行
(p.foo = o.foo)(); // 2
引用与调用的区别
调用:用原函数名调用函数,如foo()
引用:通过别名调用函数,比如var a=foo;a()
显式绑定
通过call()、apply()、bind()方法把对象绑定到this上,叫做显式绑定。对于被调用的函数来说,叫做间接调用
var a = 0;
function foo(){
console.log(this.a);
}
var obj = {
a:2
};
foo();//0
foo.call(obj);//2
显式绑定不能解决隐式丢失问题
function foo() {
setTimeout(function (){
console.log('id:',this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 });//21
new绑定
构造函数通常不使用return关键字,它们通常初始化新对象,当构造函数的函数体执行完毕时,它会显式返回。在这种情况下,构造函数调用表达式的计算结果就是这个新对象的值
function fn(){
this.a = 2;
}
var test = new fn();
console.log(test);//{a:2}
如果构造函数使用return语句但没有指定返回值,或者返回一个原始值,那么这时将忽略返回值,同时使用这个新对象作为调用结果
function fn(){
this.a = 2;
return;
}
var test = new fn();
console.log(test);//{a:2}
如果构造函数显式地使用return语句返回一个对象,那么调用表达式的值就是这个对象
var obj = {a:1};
function fn(){
this.a = 2;
return obj;
}
var test = new fn();
console.log(test);//{a:1}
以上便是this绑定的四种机制,下一篇博文将讲解箭头函数this的指向机制