成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

進(jìn)擊的 JavaScript(三) 之 函數(shù)執(zhí)行過(guò)程

netmou / 1356人閱讀

摘要:中沒(méi)有可執(zhí)行的函數(shù)了,執(zhí)行完出棧。當(dāng)某個(gè)函數(shù)被調(diào)用時(shí),會(huì)創(chuàng)建一個(gè)執(zhí)行環(huán)境及相應(yīng)的作用域鏈。檢查當(dāng)前環(huán)境中的函數(shù)聲明使用聲明的。確定指向所以說(shuō)的指向,是在函數(shù)執(zhí)行時(shí)確定的。

理解js 的執(zhí)行過(guò)程是很重要的,比如,作用域,作用域鏈,變量提升,閉包啊,要想明白這些,你就得搞懂函數(shù)執(zhí)行時(shí)到底發(fā)生了什么! 一、執(zhí)行環(huán)境(Execution Context)又稱(chēng)執(zhí)行上下文

當(dāng)代碼執(zhí)行時(shí)都會(huì)產(chǎn)生一個(gè)執(zhí)行環(huán)境。JavaScript中的執(zhí)行環(huán)境可以分為三種。

全局環(huán)境:在瀏覽器中,全局環(huán)境被認(rèn)為是window對(duì)象,因此,所有的全局變量和函數(shù)都作為window對(duì)象的 屬性 和 方法 創(chuàng)建的。

函數(shù)環(huán)境:當(dāng)一個(gè)函數(shù)執(zhí)行時(shí),就會(huì)創(chuàng)建該函數(shù)的執(zhí)行環(huán)境,在其中執(zhí)行代碼。

eval(不建議使用,可忽略)

函數(shù)內(nèi),沒(méi)有使用var 聲明的變量,在非嚴(yán)格模式下為window的屬性,即全局變量。

二、函數(shù)調(diào)用棧(call stack)

js 是根據(jù)函數(shù)的調(diào)用(執(zhí)行) 來(lái)決定 執(zhí)行順序的。每當(dāng)一個(gè)函數(shù)被調(diào)用時(shí),js 會(huì)為其創(chuàng)建執(zhí)行環(huán)境,js引擎就會(huì)把這個(gè)執(zhí)行環(huán)境 放入一個(gè)棧中 來(lái)處理。

這個(gè)棧,我們稱(chēng)之為函數(shù)調(diào)用棧(call stack)。棧底永遠(yuǎn)都是全局環(huán)境,而棧頂就是當(dāng)前正在執(zhí)行函數(shù)的環(huán)境。當(dāng)棧頂?shù)膱?zhí)行環(huán)境 執(zhí)行完之后,就會(huì)出棧,并把執(zhí)行權(quán)交給之前的執(zhí)行環(huán)境。

看栗子說(shuō)話:

function A(){
   console.log("this is A");
   function B(){
       console.log("this is B");
   }
   B();
}

A();

那么這段代碼執(zhí)行的情況就是這樣了。

首先 A() ;A 函數(shù)執(zhí)行了,A執(zhí)行環(huán)境入棧。

A函數(shù)執(zhí)行時(shí),遇到了 B(),B 又執(zhí)行了,B入棧。

B中沒(méi)有可執(zhí)行的函數(shù)了,B執(zhí)行完 出棧。

繼續(xù)執(zhí)行A, A中沒(méi)有可執(zhí)行的函數(shù)了,A執(zhí)行完 出棧。

再來(lái)個(gè)不常規(guī)的:

function A(){
    
    function B(){
        console.log(say);
    }
 
    return B;
}

var C = A();

C();

首先 A() ;A 函數(shù)執(zhí)行了,A執(zhí)行環(huán)境入棧。

繼續(xù)執(zhí)行A, A中沒(méi)有可執(zhí)行的函數(shù)了,A執(zhí)行完 出棧。

然后C(), 這時(shí)的C 就是 B,A 執(zhí)行后,把B返回 賦值給了C,B執(zhí)行環(huán)境入棧。

B中 沒(méi)有可執(zhí)行的函數(shù)了,B執(zhí)行完 出棧。

眼尖的同學(xué),估計(jì)看出來(lái)了,它怎么像閉包呢?其實(shí),稍微改動(dòng)下,它就是閉包了。

function A(){
    
    var say = 666
    
    function B(){
        console.log(say);
    }
 
    return B;
}

var C = A();

C();

//666

這就是閉包了,但是這次我們不講閉包,你就知道,它是的執(zhí)行是怎么回事就行。

三、執(zhí)行過(guò)程

現(xiàn)在我們已經(jīng)知道,每當(dāng)一個(gè)函數(shù)執(zhí)行時(shí),一個(gè)新的執(zhí)行環(huán)境就會(huì)被創(chuàng)建出來(lái)。其實(shí),在js引擎內(nèi)部,這個(gè)環(huán)境的創(chuàng)建過(guò)程可分為兩個(gè)階段:

A. 建立階段(發(fā)生在調(diào)用(執(zhí)行)一個(gè)函數(shù)時(shí),但是在執(zhí)行函數(shù)內(nèi)部的具體代碼之前)

        1.建立活動(dòng)對(duì)象;
        2.構(gòu)建作用域鏈;
        3.確定this的值。

B. 代碼執(zhí)行階段(執(zhí)行函數(shù)內(nèi)部的具體代碼)
? ? ? ?1.變量賦值;
? ? ? ?2.執(zhí)行其它代碼。

需要注意的是,作用域鏈?zhǔn)莿?chuàng)建函數(shù)的時(shí)候就創(chuàng)建了,此時(shí)的鏈只有全局變量對(duì)象,保存在函數(shù)的[[Scope]]屬性中,然后函數(shù)執(zhí)行時(shí)的,只是通過(guò)復(fù)制該屬性中的對(duì)象 來(lái) 構(gòu)建作用域鏈。本文后面還有說(shuō)明。

看圖更清晰!

如果把函數(shù)執(zhí)行環(huán)境看成一個(gè)對(duì)象的話:

executionContextObj = {           //執(zhí)行上下文對(duì)象
            AtiveObject: { },  //活動(dòng)對(duì)象
            scopeChain: { },      //作用域鏈
            this: {}              //this
}

//下面這段內(nèi)容,感興趣的可以看下,不感興趣,就跳過(guò)哈。
也許你在別家看到跟我的不一樣,人家寫(xiě)的是建立變量對(duì)象。下面我來(lái)說(shuō)說(shuō)我得想法吧!

之前我按照 首先建立變量對(duì)象,其后,變量對(duì)象轉(zhuǎn)變?yōu)榛顒?dòng)對(duì)象的規(guī)則 去理解,但是呢,通過(guò)我分析JavaScript高級(jí)程序設(shè)計(jì)第三版,4.2節(jié) 和 7.2節(jié),發(fā)現(xiàn)根本就不符合邏輯。

然后,我根據(jù)分析,得出了我的結(jié)論:變量對(duì)象 是執(zhí)行環(huán)境中保存著環(huán)境中定義的所有變量和函數(shù) 的對(duì)象 的統(tǒng)稱(chēng)。而活動(dòng)對(duì)象,是函數(shù)執(zhí)行環(huán)境中創(chuàng)建的,它不僅保存著函數(shù)執(zhí)行環(huán)境中定義的變量和函數(shù),并且獨(dú)有一個(gè)arguments 屬性。因此,活動(dòng)對(duì)象也可稱(chēng)之為變量對(duì)象。

這樣,很多東西就說(shuō)的通了。
比如(以下都是來(lái)自JavaScript高級(jí)程序設(shè)計(jì)第三版,4.2節(jié) 和 7.2節(jié) 中原文):

如果這個(gè)環(huán)境是函數(shù),則將其活動(dòng)對(duì)象(activation object)作為變量對(duì)象。活動(dòng)對(duì)象在最開(kāi)始時(shí)只包含一個(gè)變量,即 arguments 對(duì)象(這個(gè)對(duì)象在全局環(huán)境中是不存在的)。

當(dāng)某個(gè)函數(shù)被調(diào)用時(shí),會(huì)創(chuàng)建一個(gè)執(zhí)行環(huán)境(execution context)及相應(yīng)的作用域鏈。
然后,使用 arguments 和其他命名參數(shù)的值來(lái)初始化函數(shù)的活動(dòng)對(duì)象。

每個(gè)執(zhí)行環(huán)境都有一個(gè)表示變量的對(duì)象——變量對(duì)象。

此后,又有一個(gè)活動(dòng)對(duì)象(在此作為變量對(duì)象使用)被創(chuàng)建并被推入執(zhí)
行環(huán)境作用域鏈的前端。對(duì)于這個(gè)例子中 compare() 函數(shù)的執(zhí)行環(huán)境而言,其作用域鏈中包含兩個(gè)變量對(duì)象:本地活動(dòng)對(duì)象和全局變量對(duì)象。

有興趣的可以去看看這本書(shū)上說(shuō)的,有不同的想法可以積極留言,咱們好好探討,哈哈。

(一)建立階段

1、建立活動(dòng)對(duì)象(AO)

A. 建立arguments對(duì)象,檢查當(dāng)前上下文中的參數(shù),建立該對(duì)象下的屬性以及屬性值 。

B. 檢查當(dāng)前環(huán)境中的函數(shù)聲明(使用function 聲明的)。每找到一個(gè)函數(shù)聲明,就在活動(dòng)對(duì)象下面用函數(shù)名建立一個(gè)屬性,屬性值就是指向該函數(shù)在內(nèi)存中的地址的一個(gè)引用,如果上述函數(shù)名已經(jīng)存在于活動(dòng)對(duì)象下,那么則會(huì)被新的函數(shù)引用所覆蓋。

C. 檢查當(dāng)前上下文中的變量聲明(使用 var 聲明的)。每找到一個(gè)變量聲明,就在活動(dòng)對(duì)象下面用變量名建立一個(gè)屬性,該屬性值為undefined。如果該屬性名已存在,則忽略新的聲明。

function test(){
    function a(){};
    var b;
}
test();

test 函數(shù) 的活動(dòng)對(duì)象:

testAO: {    //test變量對(duì)象
    arguments: { ... };
    a:function(){};
    b:undefined
}  

變量作用域
javaScript 中,只有兩種變量作用域,一種是局部變量作用域,又稱(chēng)函數(shù)作用域。另一個(gè)則是全局作用域。

什么變量提升問(wèn)題的根本原因就在建立階段了。

console.log(A);

function A(){};

console.log(B);

var A = 666;
var B = 566;

console.log(A);
console.log(B);

//function A
//undefined
//666
//566

上面的實(shí)際順序就是這樣的了

function A(){};
//var A; 這個(gè)var 聲明的 同名 A,會(huì)被忽略

var B = undefined;

console.log(A);

console.log(B);

A = 666;   //給A 重新賦值
B = 566;   //給B 賦值

console.log(A);
console.log(B);

注意第三點(diǎn),使用var 聲明時(shí),如果VO對(duì)象下,該屬性已存在,忽略新的var 聲明。
因?yàn)锳 使用 function 聲明,VO對(duì)象下,創(chuàng)建A屬性,然后 var 聲明時(shí),檢索發(fā)現(xiàn)已經(jīng)有該屬性了,就會(huì)忽略 var A 的聲明,不會(huì)把A 設(shè)置為 undefined。

2、構(gòu)建作用域鏈
作用域鏈的最前端,始終都是當(dāng)前執(zhí)行的代碼所在函數(shù)的活動(dòng)對(duì)象。下一個(gè)AO(活動(dòng)對(duì)象)為包含本函數(shù)的外部函數(shù)的AO,以此類(lèi)推。最末端,為全局環(huán)境的變量對(duì)象。

注意:雖然作用域鏈?zhǔn)窃诤瘮?shù)調(diào)用時(shí)構(gòu)建的,但是,它跟調(diào)用順序(進(jìn)入調(diào)用棧的順序)無(wú)關(guān),因?yàn)樗桓?包含關(guān)系(函數(shù) 包含 函數(shù) 的嵌套關(guān)系) 有關(guān)。

可能比較繞口,還是來(lái)個(gè)小栗子,再來(lái)個(gè)圖

function fa(){
    var va = "this is fa";
    
    function fb(){
        var vb = "this is fb";
    
        console.log(vb);
        
        console.log(va);
    }
    return fb;
}
var fc = fa();
fc();

//"this is fb"
//"this is fa"

函數(shù)調(diào)用棧的情況就是這樣:

那么把函數(shù) fb 的執(zhí)行環(huán)境比作對(duì)象(建立階段):

fbEC = {           //執(zhí)行上下文對(duì)象

            fbAO: {   //活動(dòng)對(duì)象 AO
            
                  arguments: { ... };   //arguments 對(duì)象

                  vb: undefined   //變量聲明建立的屬性,設(shè)置為undefined
            },
            
            scopeChain: [ AO(fa), AO(fb), VO(window) ],      //作用域鏈
            
            this: { ... }              //this
}

fb作用域的展開(kāi)就是這樣的:


fb 函數(shù) 被 fa 函數(shù) 包含, fa 函數(shù) 被 window 全局環(huán)境包含。作用域鏈只跟包含關(guān)系有關(guān)!

注意:作用域鏈?zhǔn)菃蜗虻?,因此,函?shù)內(nèi)的可以訪問(wèn)函數(shù)外 和 全局的變量,函數(shù),但是反過(guò)來(lái),函數(shù)外,全局內(nèi) 不能訪問(wèn)函數(shù)內(nèi)的變量,函數(shù)。

3、確定 this 指向
所以說(shuō) this 的指向,是在函數(shù)執(zhí)行時(shí)確定的。

(二)執(zhí)行階段

1、變量賦值
根據(jù)代碼順序執(zhí)行,遇到變量賦值時(shí), 給對(duì)應(yīng)的變量賦值。

function getColor(){
    console.log(color);
    
    var color;
    console.log(color);
    
    color = "red";
    console.log(color);
}
getColor();
//undefined
//undefined
//"red";

3、執(zhí)行其他代碼。

當(dāng)函數(shù)執(zhí)行完畢后,局部活動(dòng)對(duì)象就會(huì)被銷(xiāo)毀(也就是說(shuō),局部的變量,函數(shù),arguments 等都會(huì)被銷(xiāo)毀),內(nèi)存中僅保存全局作用域(全局執(zhí)行環(huán)境的變量對(duì)象)。

這句話對(duì)理解閉包很重要,隨后,我會(huì)出一個(gè)閉包的文章,敬請(qǐng)期待!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/96050.html

相關(guān)文章

  • 進(jìn)擊JavaScript(一)變量聲明提升

    摘要:如下代碼輸出的結(jié)果是代碼執(zhí)行分為兩個(gè)大步預(yù)解析的過(guò)程代碼的執(zhí)行過(guò)程預(yù)解析與變量聲明提升程序在執(zhí)行過(guò)程中,會(huì)先將代碼讀取到內(nèi)存中檢查,會(huì)將所有的聲明在此進(jìn)行標(biāo)記,所謂的標(biāo)記就是讓解析器知道有這個(gè)名字,后面在使用名字的時(shí)候不會(huì)出現(xiàn)未定義的錯(cuò)誤。 showImg(https://segmentfault.com/img/remote/1460000012922850); 如下代碼輸出的結(jié)果是...

    LeexMuller 評(píng)論0 收藏0
  • 進(jìn)擊JavaScript)玩轉(zhuǎn)閉包

    摘要:為了更好的理解,在閱讀此文之前建議先閱讀上一篇進(jìn)擊之詞法作用域與作用域鏈?zhǔn)裁词情]包閉包的含義就是閉合,包起來(lái),簡(jiǎn)單的來(lái)說(shuō),就是一個(gè)具有封閉功能與包裹功能的結(jié)構(gòu)。在中函數(shù)構(gòu)成閉包。 為了更好的理解,在閱讀此文之前建議先閱讀上一篇《進(jìn)擊JavaScript之詞法作用域與作用域鏈》 1.什么是閉包 閉包的含義就是閉合,包起來(lái),簡(jiǎn)單的來(lái)說(shuō),就是一個(gè)具有封閉功能與包裹功能的結(jié)構(gòu)。所謂的閉包就是...

    cyixlq 評(píng)論0 收藏0
  • 進(jìn)擊JavaScript(二)詞法作用域與作用域鏈

    摘要:一作用域域表示的就是范圍,即作用域,就是一個(gè)名字在什么地方可以使用,什么時(shí)候不能使用。概括的說(shuō)作用域就是一套設(shè)計(jì)良好的規(guī)則來(lái)存儲(chǔ)變量,并且之后可以方便地找到這些變量。 一、作用域 域表示的就是范圍,即作用域,就是一個(gè)名字在什么地方可以使用,什么時(shí)候不能使用。想了解更多關(guān)于作用域的問(wèn)題推薦閱讀《你不知道的JavaScript上卷》第一章(或第一部分),從編譯原理的角度說(shuō)明什么是作用域。概...

    denson 評(píng)論0 收藏0
  • 進(jìn)擊 JavaScript(四) 閉包

    摘要:此時(shí)產(chǎn)生了閉包。導(dǎo)致,函數(shù)的活動(dòng)對(duì)象沒(méi)有被銷(xiāo)毀。是不是跟你想的不一樣其實(shí),這個(gè)例子重點(diǎn)就在函數(shù)上,這個(gè)函數(shù)的第一個(gè)參數(shù)接受一個(gè)函數(shù)作為回調(diào)函數(shù),這個(gè)回調(diào)函數(shù)并不會(huì)立即執(zhí)行,它會(huì)在當(dāng)前代碼執(zhí)行完,并在給定的時(shí)間后執(zhí)行。 上一節(jié)說(shuō)了執(zhí)行上下文,這節(jié)咱們就乘勝追擊來(lái)搞搞閉包!頭疼的東西讓你不再頭疼! 一、函數(shù)也是引用類(lèi)型的。 function f(){ console.log(not cha...

    Anleb 評(píng)論0 收藏0
  • 進(jìn)擊 JavaScript(五) 立即執(zhí)行函數(shù)與閉包

    摘要:匿名函數(shù)是不能單獨(dú)寫(xiě)的,所以就提不上立即執(zhí)行了。六立即執(zhí)行函數(shù)在閉包中的應(yīng)用立即執(zhí)行函數(shù)能配合閉包保存狀態(tài)。來(lái)看下上節(jié)內(nèi)容中閉包的例子現(xiàn)在,我們來(lái)利用立即執(zhí)行函數(shù)來(lái)簡(jiǎn)化它第一個(gè)匿名函數(shù)執(zhí)行完畢后,返回了第二個(gè)匿名函數(shù)。 前面的閉包中,提到與閉包相似的立即執(zhí)行函數(shù),感覺(jué)兩者還是比較容易弄混吧,嚴(yán)格來(lái)說(shuō)(因?yàn)橄?shū)和高程對(duì)閉包的定義不同),立即執(zhí)行函數(shù)并不屬于閉包,它不滿足閉包的三個(gè)條件。...

    vincent_xyb 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<