對this關鍵字的深入探究
this無疑是javascript中特別復雜的機制了,最使我們困擾的就是this的指向,僅以本文對我所理解的this做一個總結和記錄。每一句話都是重點!文章參考《你不知道的javascript》。
首先需要對this有一個大概的理解:每個函數的this都是在調用時被綁定的,完全取決於函數的調用位置。
綁定規則
當我們找到函數的調用位置以後,需要判斷應用了哪條綁定規則,綁定規則分為四種:默認綁定,隱式綁定,顯式綁定,new綁定。
一,默認綁定
這是最常用的函數調用類型:獨立函數調用。可以把這條規則看作是無法應用其他規則時的默認應用。
如下,這裏的foo()是直接使用不帶任何修飾的函數引用進行調用的,因此只能使用默認綁定,無法應用其他規則。
function foo() { console.log(this.a); } var a = 2; foo(); //2
註意:雖然this的綁定規則完全取決於調用位置,但是只有foo()運行在非嚴格模式下默認綁定才能綁定到全局對象。嚴格模式下與foo()的調用位置無關。如果使用嚴格模式,那麽全局對象將無法使用默認綁定,因此會綁定到undefined。
function foo() { "use strict"; console.log(this.a); } var a = 2; foo();
報錯:
二,隱式綁定
這條規則需要考慮的是調用位置是否有上下文對象
對象屬性引用鏈中只有最後一層會影響調用位置。
function foo() { console.log(this.a); }; var obj2 = { a: 32, foo: foo } var obj1 = { a: 2, obj2: obj2 }; obj1.obj2.foo(); //32
隱式丟失
這也是最常見的this綁定問題了,被隱式綁定的函數會丟失綁定對象,也就是說會應用默認綁定,從而把this綁定到全局對象或者undefined上(取決於是否嚴格模式)。
有以下幾種情況:
1.雖然bar是obj.foo的引用,但是實際上它引用的是foo函數本身,此時的bar便是一個不帶任何修飾的函數調用,因此應用了默認樣式。
function foo() { console.log(this.a); }; var obj = { a: 2, foo: foo } var bar = obj.foo; var a = "global"; bar(); //global
2.參數傳遞其實就是一種隱式賦值,因此我們傳入函數時也會被隱式賦值。
function foo() { console.log(this.a); }; function doFoo(fn) { fn(); } var obj = { a: 2, foo: foo } var a = "global"; doFoo(obj.foo); //global
3.參數傳入內置函數時也是一樣
function foo() { console.log(this.a); }; var obj = { a: 2, foo: foo } var a = "global"; setTimeout(obj.foo, 100); //global
三,顯式綁定
如果不想在對象內部包含函數引用,而想在某個對象上強制調用函數,可以使用call()和apply()方法,它們的第一個參數時對象,會把這個對象綁定到this。稱之為顯式綁定。
強制把foo的this綁定到obj,無論之後如何調用bar,總是會在obj上調用。
function foo() { console.log(this.a); }; var obj = { a: 2 } var bar = function() { foo.call(obj); } bar(); //2 setTimeout(bar, 100); //2 bar.call(window); //2
四,new綁定
使用new來調用foo時,會構造一個新的對象並把它綁定到foo()調用中的this上。
function foo(a) { this.a = a; }; var bar = new foo(2); console.log(bar.a); //2
五,優先級
既然幾種綁定規則討論完了,那麽我們來比較一下他們的優先級順序。細節不做討論,可以自行測試。總之結果是:new綁定 > 顯式綁定 > 隱式綁定 > 默認綁定。
未完待續
對this關鍵字的深入探究