傳聞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的指向機制