在對象內(nèi)部的方法中使用對象內(nèi)部的屬性是一個(gè)非常普遍的需求
但是 JavaScript 的作用域機(jī)制并不支持這一點(diǎn)删顶,基于這個(gè)需求,JavaScript 又搞出來另外一套 this 機(jī)制记舆。
var bar = {
myName:"time.geekbang.com",
printName: function () {
console.log(myName)
}
}
function foo() {
let myName = "極客時(shí)間"
return bar.printName
}
let myName = "極客邦"
let _printName = foo()
_printName()
bar.printName()
相信你已經(jīng)知道了,在 printName 函數(shù)里面使用的變量 myName 是屬于全局作用域下面的呼巴,所以最終打印出來的值都是“極客邦”泽腮。這是因?yàn)?JavaScript 語言的作用域鏈?zhǔn)怯稍~法作用域決定的,而詞法作用域是由代碼結(jié)構(gòu)來確定的衣赶。
下來咱們就展開來介紹 this诊赊,不過在講解之前,希望你能區(qū)分清楚 作用域鏈 和 this 是兩套不同的系統(tǒng)府瞄,它們之間基本沒太多聯(lián)系碧磅。在前期明確這點(diǎn),可以避免你在學(xué)習(xí) this 的過程中遵馆,和作用域產(chǎn)生一些不必要的關(guān)聯(lián)续崖。
JavaScript 中的 this 是什么
關(guān)于 this,我們還是得先從執(zhí)行上下文說起团搞。在前面幾篇文章中严望,我們提到執(zhí)行上下文中包含了變量環(huán)境、詞法環(huán)境逻恐、外部環(huán)境像吻,但其實(shí)還有一個(gè) this 沒有提及峻黍,具體你可以參考下圖:
從圖中可以看出, this 是和執(zhí)行上下文綁定 的拨匆,也就是說每個(gè)執(zhí)行上下文中都有一個(gè) this姆涩。
執(zhí)行上下文主要分為三種——全局執(zhí)行上下文、函數(shù)執(zhí)行上下文和 eval 執(zhí)行上下文
1.當(dāng)函數(shù)作為對象的方法調(diào)用時(shí)惭每,函數(shù)中的 this 就是該對象
- 當(dāng)函數(shù)被正常調(diào)用時(shí)骨饿,在嚴(yán)格模式下,this 值是 undefined台腥,非嚴(yán)格模式下 this 指向的是全局對象 window宏赘;
- 嵌套函數(shù)中的 this 不會(huì)繼承外層函數(shù)的 this 值。
- 最后黎侈,我們還提了一下箭頭函數(shù)察署,因?yàn)榧^函數(shù)沒有自己的執(zhí)行上下文,所以箭頭函數(shù)的 this 就是它外層函數(shù)的 this峻汉。
三種方式來設(shè)置函數(shù)執(zhí)行上下文中的 this 值贴汪。
1. 通過函數(shù)的 call 方法設(shè)置
2. 通過對象調(diào)用方法設(shè)置:使用對象來調(diào)用其內(nèi)部的一個(gè)方法,該方法的 this 是指向?qū)ο蟊旧淼摹?/h5>
3. 通過構(gòu)造函數(shù)中設(shè)置
function CreateObj(){
this.name = "極客時(shí)間"
}
var myObj = new CreateObj()
function CreateObj(){
this.name = "極客時(shí)間"
}
var myObj = new CreateObj()
其實(shí)休吠,當(dāng)執(zhí)行 new CreateObj() 的時(shí)候扳埂,JavaScript 引擎做了如下四件事:首先創(chuàng)建了一個(gè)空對象 tempObj;接著調(diào)用 CreateObj.call 方法瘤礁,并將 tempObj 作為 call 方法的參數(shù)聂喇,這樣當(dāng) CreateObj 的執(zhí)行上下文創(chuàng)建時(shí),它的 this 就指向了 tempObj 對象蔚携;然后執(zhí)行 CreateObj 函數(shù)希太,此時(shí)的 CreateObj 函數(shù)執(zhí)行上下文中的 this 指向了 tempObj 對象;最后返回 tempObj 對象酝蜒。
代碼解釋
var tempObj = {}
CreateObj.call(tempObj)
return tempObj
this 的設(shè)計(jì)缺陷以及應(yīng)對方案
1. 嵌套函數(shù)中的 this 不會(huì)從外層函數(shù)中繼承
var myObj = {
name : "極客時(shí)間",
showThis: function(){
console.log(this)
function bar(){console.log(this)} //window
bar()
}
}
myObj.showThis()
函數(shù) bar 中的 this 指向的是全局 window 對象誊辉,而函數(shù) showThis 中的 this 指向的是 myObj 對象
解決問題
1聲明一個(gè)變量 self 用來保存 this,然后在 bar 函數(shù)中使用 self亡脑,代碼如下所示:
這個(gè)方法的的本質(zhì)是把 this 體系轉(zhuǎn)換為了作用域的體系堕澄。
第一種是把 this 保存為一個(gè) self 變量,再利用變量的作用域機(jī)制傳遞給嵌套函數(shù)霉咨。
var myObj = {
name : "極客時(shí)間",
showThis: function(){
console.log(this)
var self = this
function bar(){
self.name = "極客邦"
}
bar()
}
}
myObj.showThis()
console.log(myObj.name)
console.log(window.name)
2你也可以使用 ES6 中的箭頭函數(shù)來解決這個(gè)問題
這是因?yàn)?ES6 中的箭頭函數(shù)并不會(huì)創(chuàng)建其自身的執(zhí)行上下文蛙紫,所以箭頭函數(shù)中的 this 取決于它的外部函數(shù)。
第二種是繼續(xù)使用 this途戒,但是要把嵌套函數(shù)改為箭頭函數(shù)坑傅,因?yàn)榧^函數(shù)沒有自己的執(zhí)行上下文,所以它會(huì)繼承調(diào)用函數(shù)中的 this喷斋。
var myObj = {
name : "極客時(shí)間",
showThis: function(){
console.log(this)
var bar = ()=>{
this.name = "極客邦"
console.log(this)
}
bar()
}
}
myObj.showThis()
console.log(myObj.name)
console.log(window.name)