解決問題:如何準(zhǔn)確判斷this指向的是什么
this是什么搀缠?
首先記住this不是指向自身铛楣!this就是一個指針,指向調(diào)用函數(shù)的對象艺普◆ぶ荩【劃重點:調(diào)用函數(shù)的對象、對象歧譬、對象0痘搿!瑰步!】
首先需要知道this的綁定規(guī)則
1矢洲、默認綁定
默認綁定 在不能應(yīng)用其它綁定規(guī)則時使用的默認規(guī)則,通常是獨立函數(shù)調(diào)用缩焦。
function sayHi() {
console.log('Hello,',this.name);
}
var name = 'ZSZ'
sayHi();
在調(diào)用sayHi()時读虏,應(yīng)用了默認綁定责静,this指向全局對象(非嚴(yán)格模式下),嚴(yán)格模式下盖桥,this指向undefined灾螃,undefined上沒有this對象,會拋出錯誤揩徊。
上面的代碼腰鬼,如果在瀏覽器環(huán)境中運行,那么結(jié)果就是 Hello,ZSZ
但是如果在node環(huán)境中運行塑荒,結(jié)果就是Hello,undefined
.這是因為node中name并不是掛在全局對象上的熄赡。
2、隱式綁定
隱式綁定函數(shù)的調(diào)用是在某個對象上觸發(fā)的袜炕,即調(diào)用位置上存在上下文對象本谜。典型的形式為 XXX.fun().
function sayHi() {
console.log('Hello,',this.name);
}
var person = {
name:'ZSZ01',
sayHi:sayHi
}
var name = 'ZSZ02';
person.sayHi();
打印的結(jié)果是Hello,ZSZ01
.
sayHi函數(shù)聲明在外部,嚴(yán)格來說并不屬于person偎窘,但是在調(diào)用sayHi時乌助,調(diào)用位置會使用person上下文來引用函數(shù),隱式綁定會把函數(shù)調(diào)用中的this(即此例sayHi函數(shù)中的this)綁定到這個上下文對象(即此例中的person)
需要注意的是:對象屬性鏈中只有最后一層會影響到調(diào)用位置陌知。例子如下:
function sayHi() {
console.log('Hello,',this.name);
}
var person2 = {
name:'Christina',
sayHi:sayHi
}
var person1 = {
name:'ZSZ'
friend:person2
}
person1.friend.sayHi();
打印是:Christina
因為只有最后一層會確定this指向的是什么他托,不管有多少層,在判斷this的時候仆葡,我們只關(guān)注最后一層赏参。即此處的friend。
隱式綁定有一個大陷阱
沿盅,綁定很容易丟失(或者說容易給我們造成誤導(dǎo)把篓,我們以為this指向的是什么,但是實際上并非如此)腰涧。
例子如下:
function sayHi() {
console.log('Hello,',this.name);
}
var person = {
name:'ZSZ01',
sayHi:sayHi
}
var name = 'ZSZ02';
var Hi = person.sayHi; // Hi指向了sayHi的引用
Hi();
打印是 ZSZ02
Hi直接指向了sayHi的引用韧掩,在調(diào)用的時候,跟person沒有半毛錢關(guān)系窖铡,針對此類問題疗锐,我們只需要急著這個格式 XXX.fn();
如果fn()前如果什么都沒有,那么肯定不是隱式綁定费彼,但是也不一定就是默認綁定滑臊。
除了上面這種丟失之外,隱式綁定的丟失是發(fā)生在回調(diào)函數(shù)中(事件回調(diào)也是其中一種)箍铲,例子如下:
function sayHi() {
console.log('Hello,',this.name);
}
var person1 = {
name:'ZSZ01',
sayHi:function(){
setTimeout(function(){
console.log('Hello,',this.name);
})
}
}
打印結(jié)果為:
Hello, Wiliam
Hello, Wiliam
Hello,
第一條輸出很容易理解雇卷,setTimeout的回調(diào)函數(shù)中,this使用的是默認綁定,非嚴(yán)格模式下关划,執(zhí)行的是全局對象
第二條輸出是不是有點迷惑了膘融?說好XXX.fun()的時候,fun中的this指向的是XXX呢祭玉,為什么這次卻不是這樣了!Why春畔?
其實這里我們可以這樣理解:setTimeout(fn,delay){fn();}
相當(dāng)于是將person2.sayHi賦值給了一個變量脱货,最后執(zhí)行了一個變量,這個時候律姨,sayHi中的this顯然和person2就沒有關(guān)系了振峻。
第三條雖然也是在setTimeout的回調(diào)中,但是我們可以看出择份,這是執(zhí)行的是person2.sayHi()使用的隱式綁定扣孟,因此這是this指向的是person2,跟當(dāng)前的作用域沒有任何關(guān)系
3荣赶、硬綁定[顯式綁定]
顯式綁定比較好理解凤价,就是通過call,apply,bind的方式,顯式的指定this所指向的對象拔创。(注意《你不知道的Javascript》中將bind單獨作為了硬綁定講解了)
call,apply和bind的第一個參數(shù)利诺,就是對應(yīng)函數(shù)的this所指向的對象。call和apply的作用一樣剩燥,只是傳參方式不同慢逾。call和apply都會執(zhí)行對應(yīng)的函數(shù),而bind方法不會灭红。
function sayHi(){
console.log('Hello,',this.name);
}
var person = {
name:'ZSZ01',
sayHi:sayHi
}
var name = 'ZSZ02';
var Hi = person.sayHi;
Hi.call(person); // Hi.apply(person)
輸出結(jié)果為: Hello,ZSZ01
因為使用硬綁定明確將this綁定在了person上侣滩。
那么,使用了硬綁定变擒,是不是意味著不會出現(xiàn)隱式綁定所遇到的綁定丟失呢?顯然不是這樣的君珠,不信,繼續(xù)往下看赁项。
function sayHi() {
console.log('Hello,',this.name);
}
var person = {
name:'ZSZ01',
sayHi:sayHi,
}
var name = 'ZSZ02';
var Hi = function(fn) {
fn();
}
Hi.call(person,person.sayHi);
輸出的結(jié)果是 Hello,ZSZ02葛躏。原因很簡單,Hi.call(person,person.sayHi);
的確是將this綁定到Hi中的this了悠菜。但是在執(zhí)行fn的時候舰攒,相當(dāng)于直接調(diào)用了sayHi方法(記住:person.sayHi已經(jīng)被賦值給fn了悔醋,隱式綁定也丟了)摩窃,沒有指定this的值,對應(yīng)的是默認綁定。
現(xiàn)在猾愿,我們希望綁定不會丟失鹦聪,要怎么做?很簡單蒂秘,調(diào)用fn的時候泽本。也給它硬綁定。
function sayHi(){
console.log('Hello,',this.name);
}
var person = {
name:'ZSZ01',
sayHi:sayHi,
}
var name = 'ZSZ02'
var Hi = function(fn) {
fn.call(this);
}
Hi.call(person,person.sayHi);
此時姻僧,輸出結(jié)果為:Hello,ZSZ01
,因為person被綁定到Hi函數(shù)中的this上规丽,fn又將這個對象綁定給了sayHi的函數(shù)。這時撇贺,sayHi中的this指向的就是person對象赌莺。
4、new綁定
javaScript和C++不一樣松嘶,并沒有類艘狭,在javaScript中,構(gòu)造函數(shù)只是使用new操作符時被調(diào)用的函數(shù)翠订,這些函數(shù)和普通的函數(shù)并沒有什么不同巢音,它不屬于某個類,也不可能實例化出一個類尽超。任何一個函數(shù)都可以使用new來調(diào)用港谊,因此其實并不存在構(gòu)造函數(shù),而只有對于函數(shù)的“構(gòu)造調(diào)用”橙弱。
使用new來調(diào)用函數(shù)歧寺,會自動執(zhí)行下面的操作:
1、創(chuàng)建一個新對象
2棘脐、將構(gòu)造函數(shù)的作用域賦值給新對象斜筐,即this指向這個新對象
3、執(zhí)行構(gòu)造函數(shù)中的代碼
4蛀缝、返回新對象
因此顷链,我們使用new來調(diào)用函數(shù)的時候,就會把新對象綁定到這個函數(shù)的this上屈梁。