要選出 js 中最讓初學(xué)者最頭疼的概念, this 必定占其中的前 3 名贺氓。不像 cpp, java 或者 python,js 在任意函數(shù)中都能使用 this戚宦。并其 this 在 js 中更類似于動(dòng)態(tài)作用域艺配。即 this 并不是在定義時(shí)決定,而是在函數(shù)調(diào)用時(shí)才決定伙菜。這種決定綁定到哪個(gè) this 的行為稱為 this 綁定轩缤。例如通過(guò)一個(gè)普通函數(shù)調(diào)用時(shí),this 實(shí)際上是綁定到全局對(duì)象(瀏覽器環(huán)境為 window贩绕,node 環(huán)境為 global):
function bar(){
this.a = 1;
}
bar()
console.info(global.a)
默認(rèn)綁定
默認(rèn)綁定是指通過(guò)調(diào)用普通函數(shù)的方式進(jìn)行綁定火的,也就是上文提到的綁定方式:
function bar(){
this.a = 1;
}
bar()
console.info(global.a) //1
隱式綁定
隱式綁定是指該函數(shù)通過(guò)對(duì)象的成員函數(shù)的方式調(diào)用,此時(shí) this 會(huì)綁定到調(diào)用的對(duì)象淑倾。例如:
global.a = 1
obj = {
a: 2,
bar: function(){
console.info(this.a)
}
}
obj.bar() // 2
注意要強(qiáng)調(diào)的一點(diǎn)是馏鹤,this 綁定始終只和調(diào)用方式有關(guān),而和定義的方式無(wú)關(guān)娇哆,例如:
global.a = 1
function bar(){
console.info(this.a)
}
obj = {
a: 2
}
obj.bar = bar
obj.bar() // 2
雖然 bar 定義在全局環(huán)境中湃累,但仍然綁定到 obj 而不是全局對(duì)象。
隱式綁定丟失
由于綁定只和調(diào)用有關(guān)碍讨,因此如果一個(gè)成員函數(shù)通過(guò)普通函數(shù)的調(diào)用方式進(jìn)行調(diào)用治力,那么它 this 就會(huì)綁定到全局變量,例如:
global.a = 1
obj = {
a: 2,
bar: function(){
console.info(this.a)
}
}
const bar = obj.bar
bar() // 1
可以看出勃黍,此時(shí) this 綁定到了全局對(duì)象宵统。還有一種更不容易發(fā)現(xiàn)的情況,即將成員函數(shù)當(dāng)成回調(diào)函數(shù)使用溉躲,由于存在一種類似的賦值行為榜田,也會(huì)產(chǎn)生對(duì)應(yīng)的丟失問(wèn)題,例如:
var a = 1
obj = {
a: 2,
bar: function(){
console.info(this.a)
}
}
setInterval(obj.bar, 1000) // 1 1 1 1 1
這就能解釋為什么在 react 中需要手動(dòng)綁定 this锻梳。
顯式綁定
通過(guò) call 或者 apply 能夠顯示的綁定對(duì)象到 this:
function bar(){
console.info(this.a)
}
bar.call({a: 1}) //1
bar.call({a: 2}) //2
硬綁定
為了避免在傳遞回調(diào)函數(shù)的時(shí)候丟失 this 綁定箭券,我們可以采用一種稱為硬綁定的方式,例如:
obj = {
a: 1,
bar: function(){
console.info(this.a)
}
}
function bind(bar, obj){
function inner(){
return bar.call(obj)
}
return inner
}
foo = bind(obj.bar, obj)
setInterval(foo, 1000)
通過(guò)實(shí)現(xiàn)一個(gè) bind 輔組函數(shù)來(lái)進(jìn)行綁定疑枯。此時(shí)辩块,就算我們將其作為一個(gè)回調(diào)函數(shù)也不會(huì)丟失 this 綁定。由于這種場(chǎng)景非常常見荆永,所以 js 內(nèi)置了 Function.prototype.bind
來(lái)實(shí)現(xiàn)硬綁定废亭。有的同學(xué)突發(fā)奇想,如果我們將一個(gè)硬綁定的函數(shù)再賦值給一個(gè)對(duì)象:
obj = {
a: 1,
bar: function(){
console.info(this.a)
}
}
obj.foo = obj.bar.bind({a: 3})
obj.foo() //3
可以看出具钥,仍然綁定到 {a: 3} 而不是 obj豆村。所以優(yōu)先級(jí)硬綁定 > 隱式綁定。
new 綁定
當(dāng)一個(gè)函數(shù)作為構(gòu)造函數(shù)使用時(shí)骂删,它會(huì)綁定到它創(chuàng)建的對(duì)象上掌动,例如:
function bar(){
this.a = 1
}
const b = new bar()
console.info(b.a) // 1
console.info(global.a) // undefined