this是 JS 這門語(yǔ)言的魅力之一——靈活方便又難以捉摸,即使是有經(jīng)驗(yàn)的程序員,如果不仔細(xì)也有可能搞錯(cuò),關(guān)于this的用法也成為許多公司的經(jīng)典面試題。
如果你寫過(guò) Java ,你可能接觸過(guò)this——一般指向當(dāng)前對(duì)象,實(shí)際上,這時(shí)候this的含義已經(jīng)確定了,因?yàn)镴ava屬于編譯期綁定,而JS屬于運(yùn)行期綁定,所以導(dǎo)致this的含義在運(yùn)行過(guò)程中可能有多種變化。
進(jìn)一步說(shuō),this和它聲明環(huán)境無(wú)關(guān),而完全取決于他的執(zhí)行環(huán)境。務(wù)必牢記這句話。
//讀以下代碼之前,必須先閱讀《哈利·波特》原著。(笑)
var name = '羅恩';
var aaa = {
name: '哈利',
say: function () {
console.log(this.name);
}
}
var bbb = {
name: '赫敏',
say: aaa.say
}
var ccc = aaa.say;
aaa.say(); //哈利
bbb.say(); //赫敏
ccc(); //羅恩
我們看第一行,aaa.say()調(diào)用的是aaa對(duì)象本身的say()方法,此時(shí)this指代的是aaa對(duì)象本身,所以此時(shí)輸出當(dāng)然就是aaa對(duì)象的name屬性值。
第二行,bbb.say();輸出赫敏一定和JS新手們的常識(shí)不相符,其實(shí)只要牢記“this取決于執(zhí)行環(huán)境”就能想明白。bbb對(duì)象是怎么聲明自身的say方法的呢?它只是把a(bǔ)aa對(duì)象的say方法引用過(guò)來(lái),注意,引用的是一個(gè)方法而非一個(gè)對(duì)象,而aaa.say存儲(chǔ)的是一個(gè)匿名函數(shù),所以這種寫法和以下代碼并沒(méi)有什么區(qū)別。
var bbb = {
name: '赫敏',
say: function () {
console.log(this.name);
}
}
第三行的ccc()是在最外層執(zhí)行,也就是在全局對(duì)象window下。所以ccc()執(zhí)行的時(shí)候this指代的就是window對(duì)象。而在window對(duì)象下聲明了name屬性,就相當(dāng)于window.name = '羅恩',輸出的當(dāng)然就是羅恩。
當(dāng)然,也有特殊情況,那就是 setTimeout 和 setInterval 。
我把開頭的aaa對(duì)象的聲明改成:
var aaa = {
name: '哈利',
getName: function () {
setTimeout(function(){
console.log(this.name);
},100)
}
}
僅僅是在console.log(this.name)外面套了一個(gè)setTimeout,猜猜原來(lái)三行的輸出會(huì)是什么?
答案:3個(gè)羅恩。
也就是說(shuō),三次this,指代的都是window對(duì)象。
關(guān)于為什么會(huì)這樣,我這里暫時(shí)不詳細(xì)展開,因?yàn)樯婕暗?strong>JS異步回調(diào)的知識(shí),如果你僅僅想快速熟悉this的用法,那么只要記住這個(gè)特殊情況即可。這個(gè)知識(shí)點(diǎn)曾經(jīng)是阿里還是小米的面試題。
顯然,三個(gè)羅恩不是我想要的,畢竟韋斯萊夫人的孩子已經(jīng)夠多了。那么我們只需稍微改寫一下這個(gè)方法:
getName: function () {
//在setTimeout外存儲(chǔ)this指代的對(duì)象
var that = this;
setTimeout(function(){
//this.name變成了that.name
console.log(that.name);
},100)
}
輸出就又正常了。
顯然,that并不是一個(gè)關(guān)鍵字,只是一個(gè)大家解決這種情況時(shí)約定俗成的名字。如果你愿意,也可以叫thatGuy。當(dāng)然,考慮到可能會(huì)有其他人維護(hù)你的代碼,還是用that比較好。
之所以寫這篇文章,是為了我下一篇文章做鋪墊:
《巧用JavaScript中apply()和call()》
敬請(qǐng)期待~~