摘要:調(diào)用棧就是為了到達(dá)當(dāng)前執(zhí)行位置所調(diào)用的所有函數(shù)。由于無(wú)法控制回調(diào)函數(shù)的執(zhí)行方式,因此就沒(méi)有辦法控制調(diào)用位置得到期望的綁定,下一節(jié)我們會(huì)介紹如何通過(guò)固定來(lái)修復(fù)這個(gè)問(wèn)題。
在《你不知道的this》中我們排除了對(duì)于this的錯(cuò)誤理解,并且明白了每個(gè)函數(shù)的this是在調(diào)用時(shí)綁定的,完全取決于函數(shù)的調(diào)用位置。在本節(jié)中我們主要介紹一下幾個(gè)主要內(nèi)容:
什么是調(diào)用位置
綁定規(guī)則
this詞法
調(diào)用位置調(diào)用位置:就是函數(shù)在代碼中被調(diào)用的位置(而不是聲明位置)。要想回答this到底引用的是什么?只有仔細(xì)分析調(diào)用位置才能回答這個(gè)問(wèn)題。
而分析調(diào)用位置最重要的就是分析調(diào)用棧。下面是調(diào)用棧的定義。
調(diào)用棧:就是為了到達(dá)當(dāng)前執(zhí)行位置所調(diào)用的所有函數(shù)。
function baz(){ //當(dāng)前調(diào)用棧是:baz //因此,當(dāng)前調(diào)用位置是全局作用域 console.log("baz"); bar(); //bar的調(diào)用位置 } function bar(){ //當(dāng)前調(diào)用棧是:baz -> bar //因此,當(dāng)前調(diào)用位置是在baz中 console.log("bar"); foo(); //foo的調(diào)用位置 } function foo(){ //當(dāng)前調(diào)用棧是:baz -> bar -> foo //因此,當(dāng)前調(diào)用位置是在baz中 console.log("foo"); } baz(); // baz的調(diào)用位置綁定規(guī)則
我們的思路是,通過(guò)找到函數(shù)的調(diào)用位置,然后判斷需要應(yīng)用規(guī)則中的哪一條。便可決定this的綁定對(duì)象。關(guān)于this的綁定規(guī)則主要是以下四種:
默認(rèn)綁定
隱式綁定
顯式綁定
new綁定
1.默認(rèn)綁定默認(rèn)綁定的典型類(lèi)型是:獨(dú)立函數(shù)調(diào)用。 思考如下代碼:
function foo(){ console.log(this.a); } var a = 2; foo(); // 2
調(diào)用foo()時(shí),函數(shù)應(yīng)用了默認(rèn)綁定,this只想全局對(duì)象window(這是在非嚴(yán)格模式下,若是在嚴(yán)格模式下會(huì)報(bào)錯(cuò)),所以this.a被解析成了全局變量a。所以,在不使用任何修飾的函數(shù)引用進(jìn)行調(diào)用,只能使用默認(rèn)綁定,無(wú)法應(yīng)用其他規(guī)則。
2.隱式綁定隱式綁定的常見(jiàn)形式是在調(diào)用位置具有上下文對(duì)象,或者說(shuō)被某個(gè)對(duì)象擁有或者包含??慈缦麓a:
function foo(){ console.log(this.a); } var obj = { a: 2, foo: foo }; obj.foo(); // 2
這里函數(shù)foo()是預(yù)先定義好的,然后再將其添加為obj對(duì)象的引用屬性。調(diào)用位置使用obj上下文來(lái)引用函數(shù),因此可以說(shuō)函數(shù)被調(diào)用時(shí)obj對(duì)象“擁有”或者“包含”它。
無(wú)論你如何稱(chēng)呼這個(gè)模式,當(dāng)foo()被調(diào)用時(shí),它的前面確實(shí)是加上了obj的引用。當(dāng)函數(shù)引用上下文對(duì)象時(shí),隱式綁定規(guī)則就會(huì)把函數(shù)調(diào)用中的this綁定到這個(gè)上下文對(duì)象。所以,this.a和obj.a是一樣的。
另一個(gè)需要注意的點(diǎn)是:對(duì)象屬性引用鏈中只有最后一層在調(diào)用位置起作用
function foo(){ console.log(this.a); } var obj2 = { a: 100, foo: foo }; var obj1 = { a: 1, obj2: obj2 } obj1.obj2.foo(); // 100
一個(gè)最常見(jiàn)的問(wèn)題就是:隱式綁定的函數(shù)會(huì)丟失綁定對(duì)象。也就是是說(shuō)它會(huì)應(yīng)用默認(rèn)綁定,而把this綁定到全局對(duì)象或者undifined上,取決于是否是嚴(yán)格模式。
function foo(){ console.log(this.a); } var obj ={ a: 2, foo: foo } var bar = obj.foo; //函數(shù)別名 var a = "global"; bar(); // "global"
雖然bar是obj.foo的一個(gè)引用,但實(shí)際上,他引用的是foo函數(shù)本身, 因此, 此時(shí)的bar()其實(shí)是一個(gè)不帶任何修飾的函數(shù)調(diào)用,因此,它應(yīng)用了默認(rèn)綁定。
一種更微妙,更常見(jiàn)的并且更出乎意料的情況發(fā)生在傳入回調(diào)函數(shù)時(shí):
function foo(){ console.log(this.a); } function callBack(fn){ fn(); } var obj = { a: 2, foo: foo } var a = "global"; callBack(obj.foo); // "global"
參數(shù)傳遞其實(shí)就是一種隱式賦值,這句話(huà)我們可以用下面的兩段代碼來(lái)詳細(xì)的講解:
var a = 1; function fn(){ alert(a); //1 a = 2; } fn(); alert(a); // 2 _ _ _ var a = 1; function fn(a){ alert(a); //undifined a = 2; } fn(); alert(a); // 1
思考一下結(jié)果是否與你想象的一致呢?
在第一段代碼中:
首先,在全局作用域中,先通過(guò)變量提升,找到了標(biāo)識(shí)符a和函數(shù)fn,a此時(shí)有個(gè)默認(rèn)值為undifined。然后,在執(zhí)行階段我們先將變量a賦值為1,緊跟著函數(shù)fn()執(zhí)行。此時(shí),在函數(shù)域中,依舊應(yīng)用變量提升的規(guī)則,但是什么都沒(méi)找到,接著執(zhí)行函數(shù)內(nèi)的代碼:alert(a),因?yàn)樵诤瘮?shù)中并沒(méi)有找到變量a。所以,通過(guò)作用域鏈向上層的父級(jí)作用域中查找,我們找到了a,并且此時(shí)a的值已經(jīng)被賦值為1,所以,alert(a)這句的結(jié)果就是1。下一句代碼:a = 2,注意a的前面沒(méi)有關(guān)鍵字var, 即這里的a是全局的,也就是說(shuō)在執(zhí)行這句代碼時(shí),他修改了全局作用域中a的值,即a現(xiàn)在為2。最后在執(zhí)行alert(a)時(shí),自然而然a的值便是2了。
在第二段代碼中:
同樣,通過(guò)變量提升,我們找到了標(biāo)識(shí)符a和函數(shù)fn,a此時(shí)的默認(rèn)值也為undifined。開(kāi)始執(zhí)行,a首先被賦值為1。然后,函數(shù)執(zhí)行,這里與第一段代碼的不同之處在于,在函數(shù)fn中傳入了參數(shù)a,那么這么做的結(jié)果就是:在函數(shù)域先運(yùn)用變量提升的規(guī)則,不會(huì)像第一段代碼中那樣什么都找不到,而是相當(dāng)于定義了一個(gè)值為undifined(調(diào)用的時(shí)候沒(méi)有傳入?yún)?shù))的變量a,所以當(dāng)執(zhí)行函數(shù)域中的alert(a)時(shí),結(jié)果就為undifined,而不會(huì)通過(guò)作用域鏈向上去查找,因?yàn)楸竞瘮?shù)中已經(jīng)找到了,只不過(guò)是以參數(shù)的形式傳入的。同理代碼(a = 2)會(huì)修改a的值,即在函數(shù)域中,a的值現(xiàn)在為2(讀者可以去嘗試在函數(shù)中最后面alert一下a的值)。而在函數(shù)外執(zhí)行alert(a),我們得到的結(jié)果便是1,因?yàn)樵摼浯a是在全局中執(zhí)行的,即會(huì)在全局中去查找變量a,而不會(huì)去訪(fǎng)問(wèn)函數(shù)域中的a。這也是因?yàn)?,在JavaSceipt中子作用域可以訪(fǎng)問(wèn)父作用域而反過(guò)來(lái)卻不行的規(guī)則。
回到我們this綁定丟失的話(huà)題上,說(shuō)了這么多,我其實(shí)就是想說(shuō):參數(shù)傳遞其實(shí)就是一種隱式賦值,參數(shù)傳遞其實(shí)就是一種隱式賦值,參數(shù)傳遞其實(shí)就是一種隱式賦值,重要的事說(shuō)三遍!
我們按照上面的方式來(lái)解析代碼:在執(zhí)行callBack(obj.foo)時(shí),在函數(shù)作用域通過(guò)變量提升找到了參數(shù)fn,它的默認(rèn)值為undifined,然后我們將參數(shù)傳入,其實(shí)相當(dāng)于(var fn = obj.foo),這就與前面的將其直接賦值給一個(gè)變量對(duì)等上了,然后再執(zhí)行fn(),應(yīng)用默認(rèn)綁定,此時(shí)的this已經(jīng)不指向obj了,而是指向window(嚴(yán)格模式)。
如果把函數(shù)傳入內(nèi)置的函數(shù)而不是傳入你自己聲明的函數(shù),會(huì)發(fā)生什么呢?結(jié)果是一樣的,沒(méi)有區(qū)別:
function foo(){ console.log(this.a) } var obj = { a: 2, foo: foo } var a = "global"; setTimeout(obj.foo, 1000); //"global"
JavaSceipt環(huán)境中內(nèi)置的setTimeout()函數(shù)實(shí)現(xiàn)和下面的偽代碼類(lèi)似:
function setTimeout(fn, delay){ //等待delay秒 fn(); //調(diào)用位置 }
就向你們看到的那樣,回調(diào)函數(shù)丟失this綁定的情況是非常常見(jiàn)的,并且還有一種情況this的行為會(huì)出乎我們意料:調(diào)用回調(diào)函數(shù)的函數(shù)可能會(huì)修改this。由于無(wú)法控制回調(diào)函數(shù)的執(zhí)行方式,因此就沒(méi)有辦法控制調(diào)用位置得到期望的綁定,下一節(jié)我們會(huì)介紹如何通過(guò)固定this來(lái)“修復(fù)“這個(gè)問(wèn)題。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/78968.html
摘要:當(dāng)我們不想再對(duì)象內(nèi)部間接包含引用函數(shù),而像在某個(gè)對(duì)象上強(qiáng)制調(diào)用函數(shù)。我們可以用中內(nèi)置的和的方法來(lái)實(shí)現(xiàn),這兩個(gè)方法的第一個(gè)參數(shù)是一個(gè)對(duì)象,是給準(zhǔn)備的,接著再調(diào)用函數(shù)時(shí)將其綁定到。 this是什么 在javascript中,每個(gè)執(zhí)行上下文可以抽象成一組對(duì)象showImg(https://segmentfault.com/img/bVuKR7); 而this是與執(zhí)行上下文相關(guān)的特殊對(duì)象,任何...
摘要:在傳統(tǒng)的面向類(lèi)的語(yǔ)言中,構(gòu)造函數(shù)是類(lèi)中的一些特殊方法,使用初始化類(lèi)是會(huì)調(diào)用類(lèi)中的構(gòu)造函數(shù)。 在上一節(jié)中我們?cè)敿?xì)介紹了this的兩種綁定方式,默認(rèn)綁定和隱式綁定,在這一節(jié)我們繼續(xù)介紹this的另外兩種綁定方式顯示綁定和new綁定。那么,我們要解決的問(wèn)題當(dāng)然就是上一節(jié)中我們提到的:this丟失! 顯式綁定 在隱式綁定中,我們必須在一個(gè)對(duì)象的內(nèi)部包含一個(gè)指向函數(shù)的屬性,并通過(guò)這個(gè)屬性間接引用...
摘要:關(guān)于的全棉解析上的文章地址判斷函數(shù)是否在中調(diào)用綁定如果是的話(huà)綁定的是新創(chuàng)建的對(duì)象。顯而易見(jiàn),這種方式可能會(huì)導(dǎo)致許多難以分析和追蹤的。默認(rèn)在嚴(yán)格模式下綁定到,否則綁定到全局對(duì)象。 關(guān)于this的全棉解析(上)的文章地址 判斷this 函數(shù)是否在new中調(diào)用(new綁定)?如果是的話(huà)this綁定的是新創(chuàng)建的對(duì)象。 bar = new foo() 函數(shù)是否通過(guò)call、apply(顯式綁定...
摘要:關(guān)于的全面解析下頁(yè)面鏈接的調(diào)用位置調(diào)用位置就是函數(shù)在代碼中被調(diào)用的位置而不是聲明的位置,尋找調(diào)用位置就是尋找函數(shù)被調(diào)用的位置,最重要的是分析調(diào)用棧就是為了到達(dá)當(dāng)前執(zhí)行位置所調(diào)用的所有函數(shù)。因此,調(diào)用函數(shù)時(shí)被綁定到這個(gè)對(duì)象上,所以和是一樣的。 關(guān)于this的全面解析(下)頁(yè)面鏈接 this的調(diào)用位置 調(diào)用位置就是函數(shù)在代碼中被調(diào)用的位置(而不是聲明的位置),尋找調(diào)用位置就是尋找函數(shù)被調(diào)用...
摘要:在嚴(yán)格模式下,對(duì)象的函數(shù)中的指向調(diào)用函數(shù)的對(duì)象實(shí)例顯式綁定,,通過(guò)可以把的綁定到上。間接引用最容易在賦值時(shí)發(fā)生返回目標(biāo)函數(shù)的引用詞法之前介紹的種綁定規(guī)則可以包含所有正常的函數(shù),但是中介紹了一種無(wú)法使用這些規(guī)則的特殊函數(shù)類(lèi)型箭頭函數(shù)。 this到底指向什么? this關(guān)鍵詞是javaScript中最復(fù)雜的機(jī)制之一,一般有兩個(gè)誤區(qū):1.this指向函數(shù)自身;2.this指向函數(shù)的作用域; ...
閱讀 4116·2023-04-26 02:07
閱讀 3742·2021-10-27 14:14
閱讀 2965·2021-10-14 09:49
閱讀 1686·2019-08-30 15:43
閱讀 2696·2019-08-29 18:33
閱讀 2432·2019-08-29 17:01
閱讀 976·2019-08-29 15:11
閱讀 678·2019-08-29 11:06