分析JS中this引發(fā)的bug
在 js 中,this 這個(gè)上下文總是變化莫測(cè),很多時(shí)候出現(xiàn)bug 總是一頭霧水,其實(shí),只要分清楚不同的情況下如何執(zhí)行就可以了,以下就是我們給大家整理的相關(guān)內(nèi)容:
在JavaScript中有一個(gè)很特別、很常用又常常讓初學(xué)者很困擾的東西 ─ “this”,在這堂課中會(huì)來(lái)談?wù)勥@個(gè)”this”。
this通常會(huì)指向一個(gè)對(duì)象,同時(shí)this會(huì)在不同的情境下指向不同的對(duì)象。讓我們來(lái)看幾個(gè)不同的情境,幫助我們更了解”this”。
window object (global object)
這里我們?cè)谌N不同情境去打印”this”,分別是在函數(shù)的最外層(outer environment)直接去執(zhí)行;使用fuction statement去執(zhí)行;使用function expression去執(zhí)行(如果還不清楚function statement和function expression的差別,可以參考注1)。
結(jié)果會(huì)發(fā)現(xiàn),這三個(gè)”this”都會(huì)指向同樣的對(duì)象,也就是global environment的window object (global object):
這也就是說(shuō),我們可以直接利用這個(gè)function和this在window object建立新的屬性:
在這里我們利用this.NewVariable = "..."
來(lái)在window object建立新的屬性,函數(shù)的最后,我們則可以直接console.log(NewVariable),這里之所以可以不用打this.NewVariable或window.NewVariable是因?yàn)槿魏卧趃lobal object (window)的屬性,我們都可以直接去使用它,而不用使用”.”。
跑出來(lái)的結(jié)果會(huì)像這樣子:
它會(huì)打印出我們的”Create a new property”,同時(shí),在window這個(gè)大的object中,我們也會(huì)找到NewVariable這個(gè)屬性:
method in object
我們知道,在對(duì)象里的值如果是原生值(primitive type;例如,字串、數(shù)值、邏輯值),我們會(huì)把這個(gè)新建立的東西稱為「屬性(property)」;如果對(duì)象里面的值是函數(shù)(function)的話,我們則會(huì)把這個(gè)新建立的東西稱為「方法(method)」。
在這里,我們就要來(lái)建立method:
首先,我們利用object literal的方式創(chuàng)建一個(gè)對(duì)象c,里面包含屬性name和方法log。log是一個(gè)匿名函數(shù)(anonymous function),函數(shù)內(nèi)容很簡(jiǎn)單,就是打印this而已(關(guān)于匿名函數(shù)可參考注1)。最后則是使用c.log的方式來(lái)執(zhí)行該方法。
讓我們來(lái)看看,這時(shí)候的”this”會(huì)是什么呢?
答案是對(duì)象c!
當(dāng)這個(gè)函數(shù)是對(duì)象里面的method時(shí),這時(shí)候的this就會(huì)指向到包含這個(gè)method的對(duì)象
JavaScript中關(guān)于this的一個(gè)bug
讓我們更進(jìn)一步延伸來(lái)看這個(gè)范例:
假設(shè)我們?cè)趍ethod log裡面多這一行this.name = "Updated Object C name"
因?yàn)槲覀冎馈眛his”現(xiàn)在指的是對(duì)象c,所以可以想像的,當(dāng)我執(zhí)行這個(gè)method的時(shí)候,它會(huì)去變更c(diǎn).name的值。
這個(gè)部分是沒(méi)有什么大問(wèn)題的,不過(guò)讓我們繼續(xù)看下去……。
假設(shè)我在method log裡面在做一些變更,我在這個(gè)method裡面,另外建立一個(gè)函數(shù)叫做setname,一樣是用this.name = newname
的方式來(lái)修改這個(gè)object c中name屬性的值。
接著執(zhí)行setname這個(gè)函數(shù),希望把object c中name的屬性值改成”New name for object c”,最后再去打印”this”來(lái)看一下。
結(jié)果我們會(huì)發(fā)現(xiàn),對(duì)象c中name屬性的值并沒(méi)有變成”New name for object c”,竟然還是一樣???怎么會(huì)這樣呢?
仔細(xì)一看,我們回來(lái)看一下我們的window object,我們會(huì)發(fā)現(xiàn),在window object中發(fā)現(xiàn)了一個(gè)新的屬性”name”,而且值是”New name for object c”。
這是什么意思呢?意思是原來(lái)我們剛剛在函數(shù)setname里面的this,指向到的是global object (window object),而不再是剛剛的object C!
我們?cè)趕etname這個(gè)function中,用console.log(this)來(lái)看一下:
在log這個(gè)method中,我們一共執(zhí)行了三次的console.log(this)結(jié)果如下:
第一個(gè)和第三個(gè)”this”指向到的是對(duì)象c,而第二個(gè)在setname中的this,指向的則是window object (global object),而這也就是為什么setname這個(gè)function沒(méi)辦法幫我們修改對(duì)象c中name屬性的名稱,因?yàn)椤眛his”根本沒(méi)指向到對(duì)象c。
而許多人都認(rèn)為,這是JavaScript的一個(gè)bug。
那么我們可以怎么做
那么碰到上述的這個(gè)例子時(shí),我們可以怎么做來(lái)避免指向到不同的對(duì)象呢?
許多人的解法是這樣的,因?yàn)槲覀冎缹?duì)象都是用的引用的方式,所以我們可以這樣做
STEP 1
我們?cè)谡麄€(gè)函數(shù)的最上面加上一行var self = this
(有些人會(huì)用var that = this
)。由于引用的特性,self和this會(huì)指向到同一個(gè)對(duì)象,而this指向?qū)ο骳,所以self一樣會(huì)指向?qū)ο骳。
STEP 2
接著,把方法log內(nèi)原本使用的”this”都改成”self”,這樣做可以確保self指向到的是c對(duì)象而不用擔(dān)心會(huì)像上面的例子一樣指向到錯(cuò)誤的對(duì)象。
結(jié)果也如同我們預(yù)期的,在第二次console.log(self)的時(shí)候,就再次替換了對(duì)象c中name屬性的值。
總結(jié)
讓我們來(lái)總結(jié)一下:
如果我們是在全局環(huán)境建立函數(shù)并打印this,這時(shí)候this會(huì)指向到全局對(duì)象,也就是window對(duì)象。
如果我們是在對(duì)象里面創(chuàng)建函數(shù),也就是方法(method)的情況時(shí),這時(shí)候的this一般就會(huì)指向到包含這個(gè)方法的對(duì)象(之所以說(shuō)”一般”是因?yàn)槌松鲜鯾ug的情況之外)。
碰到method中可能會(huì)有不知道this指向到什么的情況時(shí),為了避免不必要的錯(cuò)誤,我們可以在method中的最上面建立一個(gè)變量,去把它指定成this(var self = this)。
4.如果真的還是不知道那個(gè)情況下的this會(huì)指向到什么,就console.log出來(lái)看看吧!
示例代碼
// function statement function a(){ console.log(this); this.NewVariable = "Create a new property"; } a(); console.log(NewVariable); var c = { name:"The C object", log: function(){ var self = this; self.name = "Updated object C name"; console.log(self); var setname = function(newname){ self.name = newname; console.log(self); } setname("New name for object c"); console.log(self) } } c.log();
相關(guān)文章
基于JavaScript實(shí)現(xiàn)多級(jí)菜單效果
這篇文章主要為大家詳細(xì)介紹了基于JavaScript實(shí)現(xiàn)多級(jí)菜單效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07JavaScript獲取當(dāng)前網(wǎng)頁(yè)標(biāo)題(title)的方法
這篇文章主要介紹了JavaScript獲取當(dāng)前網(wǎng)頁(yè)標(biāo)題(title)的方法,涉及javascript中document.title方法的使用,需要的朋友可以參考下2015-04-04Javascript異步編程模型Promise模式詳細(xì)介紹
異步模式在 Web 編程中變得越來(lái)越重要,如何處理異步請(qǐng)求后的操作是一件麻煩事。Promise 是一種異步編程模型,術(shù)語(yǔ)稱作 Deferred 模式,它通過(guò)一組API來(lái)規(guī)范化異步操作,讓異步操作的流程控制更加容易。2014-05-05JavaScript高級(jí)程序設(shè)計(jì) 閱讀筆記(十九) js表格排序
js表格排序?qū)崿F(xiàn)代碼,需要的朋友可以參考下2012-08-08JSON與XML的區(qū)別對(duì)比及案例應(yīng)用
這篇文章主要介紹了JSON與XML的區(qū)別對(duì)比及案例應(yīng)用的講解。本文涉及到XML和JSON優(yōu)缺點(diǎn)講解及對(duì)比,非常不錯(cuò),具有一定的參考借鑒價(jià)值,感興趣的朋友一起看看吧2016-11-11JavaScript?數(shù)組方法filter與reduce
這篇文章主要介紹了JavaScript?數(shù)組方法filter與reduce,在ES6新增的數(shù)組方法中,包含了多個(gè)遍歷方法,其中包含了用于篩選的filter和reduce2022-07-07js canvas實(shí)現(xiàn)星空連線背景特效
這篇文章主要為大家詳細(xì)介紹了js canvas實(shí)現(xiàn)星空連線背景特效,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11js自己實(shí)現(xiàn)一個(gè)大文件切片上傳+斷點(diǎn)續(xù)傳的示例代碼
本文主要介紹了js自己實(shí)現(xiàn)一個(gè)大文件切片上傳+斷點(diǎn)續(xù)傳的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06Electron?自定義窗口桌面時(shí)鐘實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了Electron?自定義窗口桌面時(shí)鐘實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03