深刻理解JavaScript (1) —— 執行上下文與執行上下文棧

JavaScript的解析(預處理)與執行

詳見:http://www.cnblogs.com/foodoi...html

執行上下文

JavaScript在執行一個「代碼段」以前,即解析(預處理)階段,會先進行一些「準備工做」,例如掃描JS中var定義的變量、函數名等,進而生成執行上下文。閉包

JS中的「代碼段」分爲三種:全局代碼段、函數體代碼段、eval代碼段。(注:ES6以前,JS不存在「代碼塊」做用域的概念,即除了函數以外全部「{}」裏的代碼,都屬於全局做用域)函數

全局代碼段「準備工做」包括:ui

1.變量、函數表達式 —— 變量聲明,默認賦值爲undefined;
2.this —— 賦值;
3.函數聲明 —— 賦值。

函數體代碼段「準備工做」包括:this

1.變量、函數表達式 —— 變量聲明,默認賦值爲undefined;
2.this —— 賦值;
3.函數聲明 —— 賦值;
4.參數 —— 賦值;
5.argument —— 賦值;
6.自由變量的取值做用域 —— 賦值。

evel()不推薦使用,因此再也不分析evel代碼段。spa

至此,「執行上下文」的定義能夠通俗化爲 —— 在執行代碼段以前(預處理階段),把將要用到的全部變量都事先拿出來,有的直接賦值,有的先用undefined佔個空,這些變量共同組成的詞法環境,即爲執行上下文環境。code

在執行js代碼時,會有數不清的函數調用次數,會產生許多個上下文環境。這麼多上下文環境該如何管理,以及如何銷燬並釋放內存呢?這就須要「執行上下文棧」來解釋了。htm

執行上下文棧

經過上文咱們知道:預處理全局代碼時,會產生一個執行上下文環境。每次調用函數的預處理時,都會產生一個執行上下文環境。其實,當這個函數調用完成時,它的執行上下文環境以及其中的數據就會被銷燬,執行過程再從新回到全局上下文環境。同一時刻,處於活動狀態的執行上下文環境只有一個。對象

實現這一壓棧出棧過程的機制就是「執行上下文棧」。blog

圖片描述

執行上下文棧的壓棧出棧過程實例代碼:

var a = 10,                 //1.進入全局上下文環境
    fn,
    bar = function(x) {
        var b = 5;
        fn(x+b);            //3.進入fn函數上下文環境
    };

fn = function(y) {
    var c = 5;
    console.log(y+c);
}

bar(10);                    //2.進入bar函數上下文環境

預處理時,首先建立全局上下文環境:

圖片描述

而後執行代碼,全局上下文環境中的變量都被賦值:

圖片描述

當執行到調用bar函數時,跳轉到bar函數內部,對其進行預處理,建立bar函數的執行上下文環境:

圖片描述

並將這個函數上下文環境壓棧,設置爲活動狀態,開始執行bar函數體內代碼:

圖片描述

當執行到調用fn函數時,跳轉到bar函數內部,對其進行預處理,建立fn函數的執行上下文環境,並壓棧,設置爲活動狀態,開始執行fn函數體內代碼:

clipboard.png

fn函數執行完畢後,這次調用fn所生成的上下文環境出棧,而且被銷燬(已經用完了,就要及時銷燬,釋放內存),bar函數的執行上下文環境回到活動狀態:

clipboard.png

bar函數執行完畢後,調用bar函數所生成的上下文環境出棧,而且被銷燬(已經用完了,就要及時銷燬,釋放內存),全局上下文環境回到活動狀態:

clipboard.png

全局代碼執行完成,全局上下文環境出棧,而且銷燬(已經用完了,就要及時銷燬,釋放內存),代碼執行完畢。

以上是一段代碼執行上下文環境的完整變化過程,但有一種狀況的代碼,其執行上下文環境並未按上述過程銷燬,這就是接下來咱們的重點研究對象 —— 閉包

要談閉包,咱們還得先認識下做用域自由變量,敬請期待... ...

相關文章
相關標籤/搜索