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