JavaScript深刻之做用域鏈

JavaScript深刻系列第五篇,講述做用鏈的建立過程,最後結合着變量對象,執行上下文棧,讓咱們一塊兒捋一捋函數建立和執行的過程當中到底發生了什麼?前端

前言

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

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

  • 變量對象(Variable object,VO)
  • 做用域鏈(Scope chain)
  • this

今天重點講講做用域鏈。markdown

做用域鏈

《JavaScript深刻之變量對象》中講到,當查找變量的時候,會先從當前上下文的變量對象中查找,若是沒有找到,就會從父級(詞法層面上的父級)執行上下文的變量對象中查找,一直找到全局上下文的變量對象,也就是全局對象。這樣由多個執行上下文的變量對象構成的鏈表就叫作做用域鏈。閉包

下面,讓咱們以一個函數的建立和激活兩個時期來說解做用域鏈是如何建立和變化的。app

函數建立

《JavaScript深刻之詞法做用域和動態做用域》中講到,函數的做用域在函數定義的時候就決定了。函數

這是由於函數有一個內部屬性 [[scope]],當函數建立的時候,就會保存全部父變量對象到其中,你能夠理解 [[scope]] 就是全部父變量對象的層級鏈,可是注意:[[scope]] 並不表明完整的做用域鏈!oop

舉個例子:post

function foo() {
    function bar() {
        ...
    }
}複製代碼

函數建立時,各自的[[scope]]爲:this

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

bar.[[scope]] = [
    fooContext.AO,
    globalContext.VO
];複製代碼

函數激活

當函數激活時,進入函數上下文,建立 VO/AO 後,就會將活動對象添加到做用鏈的前端。

這時候執行上下文的做用域鏈,咱們命名爲 Scope:

Scope = [AO].concat([[Scope]]);複製代碼

至此,做用域鏈建立完畢。

捋一捋

如下面的例子爲例,結合着以前講的變量對象和執行上下文棧,咱們來總結一下函數執行上下文中做用域鏈和變量對象的建立過程:

var scope = "global scope";
function checkscope(){
    var scope2 = 'local scope';
    return scope2;
}
checkscope();複製代碼

執行過程以下:

1.checkscope 函數被建立,保存做用域鏈到 內部屬性[[scope]]

checkscope.[[scope]] = [
    globalContext.VO
];複製代碼

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

ECStack = [
    checkscopeContext,
    globalContext
];複製代碼

3.checkscope 函數並不馬上執行,開始作準備工做,第一步:複製函數[[scope]]屬性建立做用域鏈

checkscopeContext = {
    Scope: checkscope.[[scope]],
}複製代碼

4.第二步:用 arguments 建立活動對象,隨後初始化活動對象,加入形參、函數聲明、變量聲明

checkscopeContext = {
    AO: {
        arguments: {
            length: 0
        },
        scope2: undefined
    }
}複製代碼

5.第三步:將活動對象壓入 checkscope 做用域鏈頂端

checkscopeContext = {
    AO: {
        arguments: {
            length: 0
        },
        scope2: undefined
    },
    Scope: [AO, [[Scope]]]
}複製代碼

6.準備工做作完,開始執行函數,隨着函數的執行,修改 AO 的屬性值

checkscopeContext = {
    AO: {
        arguments: {
            length: 0
        },
        scope2: 'local scope'
    },
    Scope: [AO, [[Scope]]]
}複製代碼

7.查找到 scope2 的值,返回後函數執行完畢,函數上下文從執行上下文棧中彈出

ECStack = [
    globalContext
];複製代碼

下一篇文章

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

本文相關連接

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

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

《JavaScript深刻之變量對象》

深刻系列

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

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

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

相關文章
相關標籤/搜索