上一篇文章中講了下this的作用和部分綁定規(guī)則JavaScript中this關(guān)鍵字(上) - 簡書,這篇文章將上篇中剩下的部分說完。
this綁定規(guī)則:
3 .顯示綁定:
在靜態(tài)綁定中可以看到,必須在一個對象內(nèi)部包含一個指向函數(shù)的屬性,并通過這個屬性間接的去引用函數(shù),從而把this隱式的綁定到這個對象上。
如果不想在對象內(nèi)部包含函數(shù)的引用,而想在某個對象上強制調(diào)用函數(shù),這就是顯示綁定,怎么做才能做到顯示綁定呢?js中所有的函數(shù)都有一些公有的方法,比如call(),apply(),bind()這三種方法。那這三種方法該怎么用?首先,這三個方法的第一個參數(shù)都可以接受一個對象,它們會把對象綁定到this上,接著在調(diào)用函數(shù)時指定this,這種方法稱為顯示綁定。這三者的區(qū)別是:call()的第二個參數(shù)開始接受的是單獨的參數(shù),例如:xxx.call(obj,argument1,argument2);apply()的第二個參數(shù)開始則接受一個參數(shù)數(shù)組,例如:xxx.apply(obj,[args1,args2]);bind的第二個參數(shù)以及以后的參數(shù)加上綁定函數(shù)運行時本身的參數(shù)按照順序作為原函數(shù)的參數(shù)來調(diào)用原函數(shù)。
4.new綁定
用new的話一般是用于初始化構(gòu)造函數(shù)(類)的時候用的多一些,比如我最近在寫svg的時候就用到構(gòu)造函數(shù)(類)。使用方法如下:


在實例1中可以看到有一個svg的類,使用的時候用new就可以了。
new做了什么樣的操作呢?
1. 創(chuàng)建(或者說構(gòu)造)一個全新的對象。
2. 這個新對象會被執(zhí)行 [[ 原型 ]] 連接。
3. 這個新對象會綁定到函數(shù)調(diào)用的 this 。
4. 如果函數(shù)沒有返回其他對象,那么 new 表達式中的函數(shù)調(diào)用會自動返回這個新對象。
如上面兩張圖,在使用new來調(diào)用Svg(...)時,會構(gòu)造一個新對象并把它綁定到Svg()調(diào)用中的this上。
現(xiàn)在我們已經(jīng)大概了解了函數(shù)中調(diào)用this綁定的四條規(guī)則,我們需要做的就是找到函數(shù)的調(diào)用位置并判斷使用了那條規(guī)則。但如果某個調(diào)用位置可以應(yīng)用多條規(guī)則該怎么辦?接下來我們將探索一下綁定規(guī)則的優(yōu)先級。
毫無疑問,默認(rèn)綁定的優(yōu)先級是四條規(guī)則中最低的,我們先不考慮它
隱式綁定和顯示綁定哪個優(yōu)先級更高?上代碼

可以看到,顯示綁定的優(yōu)先級更高,也就是說在判斷時應(yīng)當(dāng)先考慮是否優(yōu)先應(yīng)用顯示綁定
那隱式綁定和new綁定哪個高呢?

可以看到new綁定要比隱式綁定優(yōu)先級高,那new綁定和顯示綁定誰的優(yōu)先級更高呢?
先回憶一下bind()是如何工作的,bind()會創(chuàng)建一個新的包裝函數(shù),這個函數(shù)會忽略它當(dāng)前的this綁定(無論綁定的對象是什么),并把提供的對象綁定到this上。這樣看起來要比new綁定的優(yōu)先級更高,無法使用new來控制this的綁定。

從實例5中可以看到,bar被綁定到了obj1上,但new bar(3)并沒有像預(yù)計的那樣把obj1.a修改為3,相反,new修改了硬綁定調(diào)用bar()的this,因為使用new的來進行綁定,會得到一個名字為baz的新對象,并且baz.a的值是3。
所以綁定規(guī)則的優(yōu)先級是:
new綁定 > 顯示綁定 >隱式綁定 >默認(rèn)綁定
不過規(guī)則總有例外,在某些特定的場景中this的綁定行為會出乎意料。
1.忽略this
不知道大家有沒有遇到過這種情況:
function foo() {
console.log( this.a );
}
var a = 2;
foo.call( null ); // 2
如果把undefined或者null傳入到call,apply或者bind中,這些值在調(diào)用時會被忽略,this會使用到默認(rèn)規(guī)則。
什么情況下會傳入null呢?
一種常見的做法就是使用apply來"展開"一個數(shù)組,并當(dāng)做參數(shù)傳入一個函數(shù)
function foo(a,b) {
console.log( "a:" + a + ", b:" + b );
}
foo.apply( null, [2, 3] ); // a:2, b:3
如果函數(shù)并不關(guān)心this的話,仍然需要傳入一個站位值,比如null.
但是,如果函數(shù)確實使用了this,那默認(rèn)綁定規(guī)則會把this綁定到全局對象(window)
2.間接引用
比如在賦值時發(fā)生的間接引用:
function foo() {
console.log(this.a);
}
vara=2;
varo={a:3,foo:foo};
varp={a:4};
o.foo();// 3
(p.foo=o.foo)();// 2
p.foo=o.foo的返回值是目標(biāo)函數(shù)的引用,因此調(diào)用位置是foo()而不是p.foo()或者o.foo(),間接引用時,this也會采取默認(rèn)綁定的規(guī)則。
3.箭頭函數(shù)
es6中提供了一個特殊函數(shù)類型:箭頭函數(shù),它不適用于上面介紹的四種規(guī)則,實際上它是根據(jù)外層(函數(shù)或者全局)的作用域來決定this的。
function foo() {
// 返回一個箭頭函數(shù)
return (a) => {
//this 繼承自 foo()
console.log( this.a );
};
}
var obj1 = {
a:2
};
var obj2 = {
a:3
};
var bar = foo.call( obj1 );
bar.call( obj2 ); // 2, 不是 3 !
箭頭函數(shù)最常用的地方在于回調(diào)函數(shù)中,例如事件處理或者定時器中。
總結(jié):
要判斷一個函數(shù)中的this指向,就需要找到這個函數(shù)的直接調(diào)用位置,找到后可以根據(jù)規(guī)則來判斷this的綁定對象
1.new調(diào)用會綁定到新創(chuàng)建的對象
2.call或者apply或者bind則綁定到指定的對象
3.上下文調(diào)用則綁定到對應(yīng)的上下文對象
4.默認(rèn)規(guī)則:嚴(yán)格模式下綁定到undefined,否則綁定到全局對象
箭頭函數(shù)并不會使用到以上四種規(guī)則,而是根據(jù)當(dāng)前的詞法作用域來決定this,也就是說,箭頭函數(shù)會繼承外層函數(shù)調(diào)用的this綁定。