[JS Behind The Scene] 從簡單例子了解 hoisting 的運作機制


Posted by Powerfultraveling 's Blog on 2021-08-21

前言

這篇文章是第十六周的功課,藉由解釋以下程式碼的運作來了解 hoisting 的機制。廢話不多說,我們開始吧!

題目

var a = 1
function fn(){
  console.log(a)
  var a = 5
  console.log(a)
  a++
  var a
  fn2()
  console.log(a)
  function fn2(){
    console.log(a)
    a = 20
    b = 100
  }
}
fn()
console.log(a)
a = 10
console.log(a)
console.log(b)

回答

這一題一樣 cosplay JS engine 跑一次!

Execution Context

engine 進入 global execution context(下稱EC)

產生了一個 varaiable object(下稱VO),裡面紀錄這個 variables 以及 function。

global:{
  VO:{
    a: undefined,
    fn: function,
  }
  scopechain: {
    global.VO
  }
}

fn() 被呼叫執行,進入 fn() 的 EC

產生了一個 fn 的 EC 並在裡面建立 fn() 的 VO 以及 scope chain。

fn:{
  VO:{
    a: undefined,
    fn2: function
  }
  scope chain:{
    自己的 scope: fn.VO,
    global 的 scopechain: global.scopechain = global.VO
  }
}

global:{
  VO:{
    a: undefined,
    fn: function,
  }
  scopechain: {
    global.VO
  }
}

fn2() 被呼叫,進入 fn2 的 EC

fn2:{
  VO:{
  }
  scopechain:{
    自己的 scope: fn2.VO,
    fn 的 scopechain: fn.scopechain = fn.scope + gloal.scope
  }
}

fn:{
  VO:{
    a: undefined,
    fn2: function
  }
  scope chain:{
    自己的 scope: fn.VO,
    global 的 scopechain: global.scopechain = global.VO
  }
}

global:{
  VO:{
    a: undefined,
    fn: function,
  }
  scopechain: {
    global.VO
  }
}

開始執行囉!

  1. 定義變數 a 為 1。
  2. 呼叫 fn()。
  3. 開始跑 fn()。
  4. 印出 a
  5. fn() 裡面雖有 a,但因為是在之後才宣告,目前 只有被 hoisting 進去 VO 裡,值仍然是 undefined, 所以第一個會印出 undefined
  6. 定義變數 a 為 5,VO 裡的 a 變為 5。
  7. 印出 a,這次 a 的值為 5,所以印出 5
  8. 執行 a++,所以 a 變為 6。
  9. 宣告 a ,但 a 在前面早被宣告了,所以這邊的操作無效直接跳過。
  10. 呼叫 fn2()。
  11. 印出 a,fn2() 的 VO 裡沒有 a,所以沿著 scopechain 往上找,在 fn() 裡找到變為 6 的 a,所以印出 6
  12. a 的值改為 20,但 fn2() 裡沒有 a,所以一樣沿著 scopechain 往上找,在 fn() 裡找到變為 6 的 a,改變成 20。
  13. b 的值改為 100,但是沿著 scopechain 往上找,發現根本沒有任何 EC 裡有宣告過 b,所以 b 直接變成值為 100 的全域變數,換言之,在 global 裡的 VO 加上變數 b,且值為 100。
  14. 印出 a 值,此時的 a 值已經被改為 20,所以印出20
  15. 印出 a 值,因為已經回到 global 的 EC 裡,所以在 fn() 裡面一大塊裡對 a 的操作已經無關,只需要檢查 a 在 global 的 VO 裡的值,得出答案為 1,所以印出 1
  16. a 的值改為 10。
  17. 印出 a,10
  18. 印出 b, 100

對答案


#hoisting #variable-object #execution-context







Related Posts

BootStrap5 第二章 - 排版(格線系統 )

BootStrap5 第二章 - 排版(格線系統 )

JS 時間日期處理

JS 時間日期處理

用C++做資料分析 | CERN ROOT 教學[02] - 資料匯入與簡單分析

用C++做資料分析 | CERN ROOT 教學[02] - 資料匯入與簡單分析


Comments