JavaScript深刻之執行上下文

JavaScript深刻系列第七篇,結合以前所講的四篇文章,以權威指南的demo爲例,具體講解當函數執行的時候,執行上下文棧、變量對象、做用域鏈是如何變化的。git

前言

《JavaScript深刻之執行上下文棧》中講到,當 JavaScript 代碼執行一段可執行代碼(executable code)時,會建立對應的執行上下文(execution context)。github

對於每一個執行上下文,都有三個重要屬性:面試

  • 變量對象(Variable object,VO)閉包

  • 做用域鏈(Scope chain)app

  • this函數

而後分別在《JavaScript深刻之變量對象》《JavaScript深刻之做用域鏈》《JavaScript深刻之從ECMAScript規範解讀this》中講解了這三個屬性。ui

閱讀本文前,若是對以上的概念不是很清楚,但願先閱讀這些文章。this

由於,這一篇,咱們會結合着全部內容,講講執行上下文的具體處理過程。code

思考題

《JavaScript深刻之詞法做用域和動態做用域》中,提出這樣一道思考題:對象

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f();
}
checkscope();
var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();

兩段代碼都會打印'local scope'。雖然兩段代碼執行的結果同樣,可是兩段代碼究竟有哪些不一樣呢?

緊接着就在下一篇《JavaScript深刻之執行上下文棧》中,講到了二者的區別在於執行上下文棧的變化不同,然而,若是是這樣籠統的回答,依然顯得不夠詳細,本篇就會詳細的解析執行上下文棧和執行上下文的具體變化過程。

具體執行分析

咱們分析第一段代碼:

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f();
}
checkscope();

執行過程以下:

1.執行全局代碼,建立全局執行上下文,全局上下文被壓入執行上下文棧

ECStack = [
        globalContext
    ];

2.全局上下文初始化

globalContext = {
        VO: [global, scope, checkscope],
        Scope: [globalContext.VO],
        this: globalContext.VO
    }

2.初始化的同時,checkscope 函數被建立,保存做用域鏈到函數的內部屬性[[scope]]

checkscope.[[scope]] = [
      globalContext.VO
    ];

3.執行 checkscope 函數,建立 checkscope 函數執行上下文,checkscope 函數執行上下文被壓入執行上下文棧

ECStack = [
        checkscopeContext,
        globalContext
    ];

4.checkscope 函數執行上下文初始化:

  1. 複製函數 [[scope]] 屬性建立做用域鏈,

  2. 用 arguments 建立活動對象,

  3. 初始化活動對象,即加入形參、函數聲明、變量聲明,

  4. 將活動對象壓入 checkscope 做用域鏈頂端。

同時 f 函數被建立,保存做用域鏈到 f 函數的內部屬性[[scope]]

checkscopeContext = {
        AO: {
            arguments: {
                length: 0
            },
            scope: undefined,
            f: reference to function f(){}
        },
        Scope: [AO, globalContext.VO],
        this: undefined
    }

5.執行 f 函數,建立 f 函數執行上下文,f 函數執行上下文被壓入執行上下文棧

ECStack = [
        fContext,
        checkscopeContext,
        globalContext
    ];

6.f 函數執行上下文初始化, 如下跟第 4 步相同:

  1. 複製函數 [[scope]] 屬性建立做用域鏈

  2. 用 arguments 建立活動對象

  3. 初始化活動對象,即加入形參、函數聲明、變量聲明

  4. 將活動對象壓入 f 做用域鏈頂端

fContext = {
        AO: {
            arguments: {
                length: 0
            }
        },
        Scope: [AO, checkscopeContext.AO, globalContext.VO],
        this: undefined
    }

7.f 函數執行,沿着做用域鏈查找 scope 值,返回 scope 值

8.f 函數執行完畢,f 函數上下文從執行上下文棧中彈出

ECStack = [
        checkscopeContext,
        globalContext
    ];

9.checkscope 函數執行完畢,checkscope 執行上下文從執行上下文棧中彈出

ECStack = [
        globalContext
    ];

第二段代碼就留給你們去嘗試模擬它的執行過程。

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();

不過,在下一篇《JavaScript深刻之閉包》中也會說起這段代碼的執行過程。

下一篇文章

《JavaScript深刻之閉包》

相關連接

《JavaScript深刻之詞法做用域和動態做用域》

《JavaScript深刻之執行上下文棧》

《JavaScript深刻之變量對象》

《JavaScript深刻之做用域鏈》

《JavaScript深刻之從ECMAScript規範解讀this》

重要參考

《一道js面試題引起的思考》

本文寫的太好,給了我不少啓發。感激涕零!

深刻系列

JavaScript深刻系列目錄地址:https://github.com/mqyqingfeng/Blog

JavaScript深刻系列預計寫十五篇左右,旨在幫你們捋順JavaScript底層知識,重點講解如原型、做用域、執行上下文、變量對象、this、閉包、按值傳遞、call、apply、bind、new、繼承等難點概念。

若是有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。若是喜歡或者有所啓發,歡迎star,對做者也是一種鼓勵。

相關文章
相關標籤/搜索