JS進(jìn)階筆記(一)——執(zhí)行上下文

前言

本文是學(xué)習(xí)執(zhí)行上下文時(shí)做的學(xué)習(xí)筆記,非原創(chuàng),只是對(duì)知識(shí)的整理。
參考資料:

什么是執(zhí)行上下文?

執(zhí)行上下文是 評(píng)估和執(zhí)行 js 代碼的環(huán)境 的抽象概念。每當(dāng) js 代碼在運(yùn)行的時(shí)候,它都是在執(zhí)行上下文中運(yùn)行。

每個(gè)執(zhí)行上下文都有三個(gè)重要屬性:

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

執(zhí)行上下文的三種類型

  • 全局執(zhí)行上下文 —— 任何不在函數(shù)內(nèi)部的代碼都在全局上下文中。它會(huì)執(zhí)行兩件事:創(chuàng)建一個(gè)全局的 window 對(duì)象(瀏覽器的情況下),并且設(shè)置 this 的值等于這個(gè)全局對(duì)象。一個(gè)程序中只會(huì)有一個(gè)全局執(zhí)行上下文。
  • 函數(shù)執(zhí)行上下文 —— 每一個(gè)函數(shù)被調(diào)用時(shí),都會(huì)為該函數(shù)創(chuàng)建一個(gè)新的上下文。每個(gè)函數(shù)都有自己的執(zhí)行上下文,不過是在函數(shù)被調(diào)用時(shí)創(chuàng)建的。函數(shù)上下文可以有任意多個(gè)。

    在函數(shù)上下文中,我們用 活動(dòng)對(duì)象 (activation object, AO) 來表示變量對(duì)象。

    活動(dòng)對(duì)象和變量對(duì)象其實(shí)是一個(gè)東西,只是變量對(duì)象是規(guī)范上的或者說是引擎實(shí)現(xiàn)上的,不可在 JavaScript 環(huán)境中訪問,只有到當(dāng)進(jìn)入一個(gè)執(zhí)行上下文中,這個(gè)執(zhí)行上下文的變量對(duì)象才會(huì)被激活,所以才叫 activation object ,而只有被激活的變量對(duì)象,也就是活動(dòng)對(duì)象上的各種屬性才能被訪問。

    活動(dòng)對(duì)象是在進(jìn)入函數(shù)上下文時(shí)刻被創(chuàng)建的,它通過函數(shù)的 arguments 屬性初始化。arguments 屬性值是 Arguments 對(duì)象。

  • Eval 函數(shù)執(zhí)行上下文 —— eval() 函數(shù)可計(jì)算某個(gè)字符串,并執(zhí)行其中的 js 代碼。 執(zhí)行在 eval 函數(shù)內(nèi)部的代碼葉惠有它自己的執(zhí)行上下文。不常用。

執(zhí)行棧

執(zhí)行棧,也在其他編程語言中稱為“調(diào)用?!保且环N擁有 LIFO (后進(jìn)先出)數(shù)據(jù)結(jié)構(gòu)的棧,被用來 存儲(chǔ)代碼運(yùn)行時(shí)創(chuàng)建的所有執(zhí)行上下文 。

當(dāng) js 引擎第一次遇到你的腳本時(shí),它會(huì)創(chuàng)建一個(gè)全局的執(zhí)行上下文并且壓入當(dāng)前執(zhí)行棧。每當(dāng)引擎遇到一個(gè)函數(shù)調(diào)用,它會(huì)為該函數(shù)創(chuàng)建一個(gè)新的執(zhí)行上下文并壓入棧的頂部。

引擎會(huì)執(zhí)行那些執(zhí)行上下文位于棧頂?shù)暮瘮?shù)。當(dāng)該函數(shù)執(zhí)行結(jié)束時(shí),執(zhí)行上下文從棧中彈出,控制流程到達(dá)當(dāng)前棧中的下一個(gè)上下文。

執(zhí)行上下文的生命周期

創(chuàng)建

  • 生成變量對(duì)象
  • 創(chuàng)建作用域鏈
  • 確定 this指向

執(zhí)行

  • 變量賦值

執(zhí)行完畢后出棧,等待被回收

創(chuàng)建階段

在 js 代碼執(zhí)行前,執(zhí)行上下文將經(jīng)歷創(chuàng)建階段。在創(chuàng)建階段會(huì)發(fā)生三件事:

  1. this 綁定

    在全局執(zhí)行上下文中,this 的值指向全局對(duì)象。在瀏覽器中,this 引用 Window 對(duì)象。

    在函數(shù)執(zhí)行上下文中,this 的值取決于該函數(shù)是如何被調(diào)用的。如果它被一個(gè)引用對(duì)象調(diào)用,那么 this 會(huì)被設(shè)置成那個(gè)對(duì)象,否則 this 的值就是全局對(duì)象或者 undefined(嚴(yán)格模式下)。例如:

    let foo = {
      baz: function() {
        console.log(this);
      }
    }
    
    foo.baz(); // 'foo' 
    
    let bar = foo.baz;
    
    bar(); // window 因?yàn)闆]有指定引用對(duì)象
    
  1. 創(chuàng)建 詞法環(huán)境 組件

    ES6文檔定義:

    詞法環(huán)境 是一種規(guī)范類型,基于 ECMAScript 代碼的詞法嵌套結(jié)構(gòu)來定義標(biāo)識(shí)符和具體變量和函數(shù)的關(guān)聯(lián)。一個(gè)詞法環(huán)境由環(huán)境記錄器和一個(gè)可能的引用外部詞法環(huán)境的空值組成。

    簡(jiǎn)單來說 詞法環(huán)境 是一種持有標(biāo)識(shí)符—變量映射的結(jié)構(gòu)。(這里的標(biāo)識(shí)符指的是變量/函數(shù)的名字,而變量是對(duì)實(shí)際對(duì)象[包含函數(shù)類型對(duì)象]或原始數(shù)據(jù)的引用)。

    現(xiàn)在,在詞法環(huán)境的內(nèi)部有兩個(gè)組件:(1) 環(huán)境記錄器和 (2) 一個(gè)外部環(huán)境的引用。

    1. 環(huán)境記錄器是存儲(chǔ)變量和函數(shù)聲明的實(shí)際位置。
    2. 外部環(huán)境的引用意味著它可以訪問其父級(jí)詞法環(huán)境(作用域)。

    詞法環(huán)境有兩種類型:

    • 全局環(huán)境(在全局執(zhí)行上下文中)是沒有外部環(huán)境引用的詞法環(huán)境。全局環(huán)境的外部環(huán)境引用是 null。它擁有內(nèi)建的 Object/Array/等、在環(huán)境記錄器內(nèi)的原型函數(shù)(關(guān)聯(lián)全局對(duì)象,比如 window 對(duì)象)還有任何用戶定義的全局變量,并且 this的值指向全局對(duì)象。
    • 函數(shù)環(huán)境中,函數(shù)內(nèi)部用戶定義的變量存儲(chǔ)在環(huán)境記錄器中。并且引用的外部環(huán)境可能是全局環(huán)境,或者任何包含此內(nèi)部函數(shù)的外部函數(shù)。

    環(huán)境記錄器也有兩種類型(如上?。?/p>

    1. 聲明式環(huán)境記錄器存儲(chǔ)變量、函數(shù)和參數(shù)。
    2. 對(duì)象環(huán)境記錄器用來定義出現(xiàn)在全局上下文中的變量和函數(shù)的關(guān)系。

簡(jiǎn)而言之,

  • 全局環(huán)境中,環(huán)境記錄器是對(duì)象環(huán)境記錄器。
  • 函數(shù)環(huán)境中,環(huán)境記錄器是聲明式環(huán)境記錄器。

注意 — 對(duì)于函數(shù)環(huán)境聲明式環(huán)境記錄器還包含了一個(gè)傳遞給函數(shù)的 arguments 對(duì)象(此對(duì)象存儲(chǔ)索引和參數(shù)的映射)和傳遞給函數(shù)的參數(shù)的 length。

  1. 創(chuàng)建 變量環(huán)境 組件

    它同樣是一個(gè)詞法環(huán)境,其環(huán)境記錄器持有變量聲明語句在執(zhí)行上下文中創(chuàng)建的綁定關(guān)系。

    如上所述,變量環(huán)境也是一個(gè)詞法環(huán)境,所以它有著上面定義的詞法環(huán)境的所有屬性。

    在 ES6 中,詞法環(huán)境組件和變量環(huán)境的一個(gè)不同就是前者被用來存儲(chǔ)函數(shù)聲明和變量(letconst)綁定,而后者只用來存儲(chǔ) var 變量綁定。

因此,變量對(duì)象會(huì)包括

  1. 函數(shù)的所有形參 (如果是函數(shù)上下文)
    • 由名稱和對(duì)應(yīng)值組成的一個(gè)變量對(duì)象的屬性被創(chuàng)建
    • 沒有實(shí)參,屬性值設(shè)為 undefined
  2. 函數(shù)聲明
    • 由名稱和對(duì)應(yīng)值(函數(shù)對(duì)象(function-object))組成一個(gè)變量對(duì)象的屬性被創(chuàng)建
    • 如果變量對(duì)象已經(jīng)存在相同名稱的屬性,則完全替換這個(gè)屬性
  3. 變量聲明
    • 由名稱和對(duì)應(yīng)值(undefined)組成一個(gè)變量對(duì)象的屬性被創(chuàng)建;
    • 如果變量名稱跟已經(jīng)聲明的形式參數(shù)或函數(shù)相同,則變量聲明不會(huì)干擾已經(jīng)存在的這類屬性
執(zhí)行階段

在此階段,順序執(zhí)行代碼,完成對(duì)所有這些變量的分配,修改變量對(duì)象的值。

在執(zhí)行階段,如果 js 引擎不能在源碼中聲明的實(shí)際位置找到 let 變量的值,它會(huì)被賦值為 undefined

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容