言
最近正在看《你不知道的JavaScript》,里面關(guān)于this綁定機(jī)制的部分講的特別好,很清晰,這部分對(duì)我們js的使用也是相當(dāng)關(guān)鍵的,并且這也是一個(gè)面試的高頻考點(diǎn),所以整理一篇文章分享一下這部分的內(nèi)容,相信看本文的解析,你一定會(huì)有所收獲的,如果喜歡的話可以點(diǎn)波贊/關(guān)注,支持一下。
個(gè)人博客了解一下:obkoro1.com
為什么要用this:
function identify() {
console.log("Hello,I'm " + this.name);
}
let me = {
name: "Kyle"
};
let you = {
name: "Reader"
};
identify.call(me); // Hello,I'm Kyle
identify.call(you); // Hello,I'm Reader
這個(gè)簡(jiǎn)單的栗子,可以在不同的對(duì)象中復(fù)用函數(shù)identify,不用針對(duì)每個(gè)對(duì)象編寫(xiě)一個(gè)新函數(shù)。
this解決的問(wèn)題:
this提供了一種更優(yōu)雅的方法來(lái)隱式'傳遞'一個(gè)對(duì)象的引用,因此可以將API設(shè)計(jì)得更加簡(jiǎn)潔并且易于復(fù)用。
this的四種綁定規(guī)則:
默認(rèn)綁定:
規(guī)則:在非嚴(yán)格模式下,默認(rèn)綁定的this指向全局對(duì)象,嚴(yán)格模式下this指向undefined
function foo() {
console.log(this.a); // this指向全局對(duì)象
}
var a = 2;
foo(); // 2
function foo2() {
"use strict"; // 嚴(yán)格模式this綁定到undefined
console.log(this.a);
}
foo2(); // TypeError:a undefined
默認(rèn)綁定規(guī)則如上述栗子,書(shū)中還提到了一個(gè)微妙的細(xì)節(jié):
function foo() {
console.log(this.a); // foo函數(shù)不是嚴(yán)格模式 默認(rèn)綁定全局對(duì)象
}
var a = 2;
function foo2(){
"use strict";
foo(); // 嚴(yán)格模式下調(diào)用其他函數(shù),不影響默認(rèn)綁定
}
foo2(); // 2
所以:對(duì)于默認(rèn)綁定來(lái)說(shuō),決定this綁定對(duì)象的是函數(shù)體是否處于嚴(yán)格模式,嚴(yán)格指向undefined,非嚴(yán)格指向全局對(duì)象。
通常不會(huì)在代碼中混用嚴(yán)格模式和非嚴(yán)格模式,所以這種情況很罕見(jiàn),知道一下就可以了,避免某些變態(tài)的面試題挖坑。
隱式綁定:
規(guī)則:函數(shù)在調(diào)用位置,是否有上下文對(duì)象,如果有,那么this就會(huì)隱式綁定到這個(gè)對(duì)象上。
function foo() {
console.log(this.a);
}
var a = "Oops, global";
let obj2 = {
a: 2,
foo: foo
};
let obj1 = {
a: 22,
obj2: obj2
};
obj2.foo(); // 2 this指向調(diào)用函數(shù)的對(duì)象
obj1.obj2.foo(); // 2 this指向最后一層調(diào)用函數(shù)的對(duì)象
// 隱式綁定丟失
let bar = obj2.foo; // bar只是一個(gè)函數(shù)別名 是obj2.foo的一個(gè)引用
bar(); // "Oops, global" - 指向全局
隱式綁定丟失:
隱式綁定丟失的問(wèn)題:實(shí)際上就是函數(shù)調(diào)用時(shí),并沒(méi)有上下文對(duì)象,只是對(duì)函數(shù)的引用,所以會(huì)導(dǎo)致隱式綁定丟失。
同樣的問(wèn)題,還發(fā)生在傳入回調(diào)函數(shù)中,這種情況更加常見(jiàn),并且隱蔽,類似:
test(obj2.foo); // 傳入函數(shù)的引用,調(diào)用時(shí)也是沒(méi)有上下文對(duì)象。
顯式綁定:
就像我們上面看到的,如果單純使用隱式綁定肯定沒(méi)有辦法得到期望的綁定,幸好我們還可以在某個(gè)對(duì)象上強(qiáng)制調(diào)用函數(shù),從而將this綁定在這個(gè)對(duì)象上。
規(guī)則:我們可以通過(guò)apply、call、bind將函數(shù)中的this綁定到指定對(duì)象上。
function foo() {
console.log(this.a);
}
let obj = {
a: 2
};
foo.call(obj); // 2
傳入的不是對(duì)象:
如果你傳入了一個(gè)原始值(字符串,布爾類型,數(shù)字類型),來(lái)當(dāng)做this的綁定對(duì)象,這個(gè)原始值轉(zhuǎn)換成它的對(duì)象形式。
如果你把null或者undefined作為this的綁定對(duì)象傳入call/apply/bind,這些值會(huì)在調(diào)用時(shí)被忽略,實(shí)際應(yīng)用的是默認(rèn)綁定規(guī)則。
new綁定:
書(shū)中提到:在js中,實(shí)際上并不存在所謂的'構(gòu)造函數(shù)',只有對(duì)于函數(shù)的'構(gòu)造調(diào)用'。
new的時(shí)候會(huì)做哪些事情:
- 創(chuàng)建一個(gè)全新的對(duì)象。
- 這個(gè)新對(duì)象會(huì)被執(zhí)行 [[Prototype]] 連接。
- 這個(gè)新對(duì)象會(huì)綁定到函數(shù)調(diào)用的this。
- 如果函數(shù)沒(méi)有返回其他對(duì)象,那么new表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新對(duì)象。
規(guī)則:使用構(gòu)造調(diào)用的時(shí)候,this會(huì)自動(dòng)綁定在new期間創(chuàng)建的對(duì)象上。
function foo(a) {
this.a = a; // this綁定到bar上
}
let bar = new foo(2);
console.log(bar.a); // 2
this四種綁定規(guī)則的優(yōu)先級(jí)
如果在某個(gè)調(diào)用位置應(yīng)用了多條規(guī)則,如何確定哪條規(guī)則生效?
obj.foo.call(obj2); // this指向obj2 顯式綁定比隱式綁定優(yōu)先級(jí)高。
new obj.foo(); // thsi指向new新創(chuàng)建的對(duì)象 new綁定比隱式綁定優(yōu)先級(jí)高。
顯式綁定和new綁定無(wú)法直接比較(會(huì)報(bào)錯(cuò)),默認(rèn)綁定是不應(yīng)用其他規(guī)則之后的兜底綁定所以優(yōu)先級(jí)最低,最后的結(jié)果是:
顯式綁定 > 隱式綁定 > 默認(rèn)綁定
new綁定 > 隱式綁定 > 默認(rèn)綁定
箭頭函數(shù)的this指向不會(huì)使用上述的四條規(guī)則:
function foo() {
return () => {
console.log(this.a);
};
}
let obj1 = {
a: 2
};
let obj2 = {
a: 22
};
let bar = foo.call(obj1); // foo this指向obj1
bar.call(obj2); // 輸出2 這里執(zhí)行箭頭函數(shù) 并試圖綁定this指向到obj2
從上述栗子可以得出,箭頭函數(shù)的this規(guī)則:
-
箭頭函數(shù)中的
this繼承于它外面第一個(gè)不是箭頭函數(shù)的函數(shù)的this指向。 - 箭頭函數(shù)的 this 一旦綁定了上下文,就不會(huì)被任何代碼改變。
結(jié)語(yǔ)
認(rèn)真看完的話,相信你已經(jīng)get到this的用法了,最后推薦一下《你不知道的JavaScript》,這本書(shū)真的很好,寫(xiě)的也很有趣,沒(méi)看過(guò)的小伙伴抓緊入手了。