[ 筆記 ] JavaScript 進階 03 - Execution Context - Variable Object


Posted by krebikshaw on 2020-09-25

Execution Context 執行環境

JavaScript 所有的程式碼都要在 執行環境 Execution Context 中執行,我們要搞懂 JavaScript 的行為之前,需要先了解 Execution Context 是如何運作的。

當我們要準備運行 JavaScript 程式碼時,第一個 Execution Context 就是全域物件的 Execution Context,簡稱 Global EC。

之後每建立一個新的執行環境時,這些新的 EC 就會以 call stack 的方式進行堆疊。(如果不知道 call stack 是什麼,可參考[ 筆記 ] JavaScript 進階 - Event Loop

Execution Context 的內容有哪些?

你可以想像成 JavaScript 的物件,裡面的內容會有 VO 跟 scopeChain 跟 this:

EC: {
    Variable Object (VO): {
        arguments: {
            ... 
        },
        FD: <reference to function>
        <variable>: <value>,
    },
    scopeChain: [...],
    this: ...
}

☞ VO / AO: Variable Object

用來初始化變數的地方,會將被宣告的變數及 function 紀錄在這邊

只有在 globalEC 裡的稱為 VO,而 functionEC 裡會叫 AO,其差異就是創建 AO 時會初始化一欄 arguments: <ArgO>

VO / AO 會用來紀錄物件的 arguments & variable & function。
每當一個新的執行環境被建立時(程式開始執行 or 被呼叫的函式開始執行),就會先初始化這個執行環境的 VO / AO,整理好所需要用到的變數及函式

我們以一個簡單的例子作為範例:

function add(a, b) {
  return a + b;
}

add(3, 2)

// add 被呼叫時,進入 add 的執行環境,建立 AO 來初始化變數
AO: {
  a: 3,
  b: 2
}

剛剛舉得是有參數傳進去時的初始化,那如果情況變成在 function 裡面才宣告變數,變數會先被加進 AO 裡面並且初始化為 undefined,等到程式執行到賦值的那一行,AO 底下的變數才會有數值

function func() {
  console.log(a);  // undefined
  var a = 10;
}

func()

// func 被呼叫時,進入 func 的執行環境,a 會被加到 AO 並初始化為 undefined 
AO: {
  a: undefined
}
//  所以此時 console.log 印出來的值是 undefined

================以上部分完成初始化,下面為開始執行之後===================

//  等到程式執行到 a = 10 的這行時,才會更新 AO
AO {
  a: 10
}

如果 function 裡面有宣告 function,也會把它加進 AO,值得注意的是,如果宣告的 function 名稱跟其他變數名稱撞名,function 就會把變數蓋掉

function func() {
  var a = 10;
  console.log(a);
  function a() {...}  // a() 與 var a 撞名
}

AO: {
  a: function  // 撞名的情況,function 會把變數蓋掉
}

變數初始化的順序

當進入新的執行環境時,變數跟函式會被放進 AO 進行初始化,而這個初始化順序如下:

  1. argument 先放進 AO,並初始化為 argument 的數值
  2. function 放進 AO,遇到同名的變數會覆蓋過去
  3. variable 放進 AO,並初始化為 undefined
function test(a, b) {
  function b() {...}
  var c = 30;
}

test(123);

// argument 先放進 AO,並初始化為 argument 的數值
AO: {
  a: 123,
  b: undefined
}

// function 放進 AO,遇到同名的變數會覆蓋過去
AO: {
  a: 123,
  b: function
}

// variable 放進 AO,並初始化為 undefined
AO: {
  a: 123,
  b: function,
  c: undefined
}

================以上部分完成初始化,下面為開始執行之後===================

// 等到程式執行到 c = 30 的這行時,才會更新 AO
AO: {
  a: 123,
  b: function,
  c: 30
}

小補充:如果在 function 裡面直接賦值一個沒有被宣告的變數,這個變數會直接被宣告在 globalEC 的 VO

function test() {
  a = 10;  // 直接賦值一個沒有被宣告的變數
}

console.log(a);  // 10

系統會把 a 宣告在 globalEC 的 VO,所以在 function 外面 console.log 會印出 10

let & const 的 TDZ 特性

let 跟 const 宣告的變數也會加到 VO / AO 並初始化為 undefined,但是它們有一個極大的差異性,就是用 let 或者 const 選告的變數,在賦值之前都不可以去存取它,否則會報錯

function test() {
  console.log(a);  // a is not defined
  let a = 10;  // 用 let 或者 const 選告的變數,在賦值之前都不可以去存取它
}

test()

在宣告到賦值之間這段不能被存取的過程被稱作 Temporal Dead Zone

Function expression 的初始化(問題與討論)

可以參考:https://github.com/Lidemy/mentor-program-3rd-ClayGao/pull/24


#Execution Context #Variable Object







Related Posts

W11_作業一實作_完成留言板

W11_作業一實作_完成留言板

D3_交作業流程跟心態培養

D3_交作業流程跟心態培養

React Form: Redux Form vs React Final Form vs Formik and Yup

React Form: Redux Form vs React Final Form vs Formik and Yup


Comments