Hoisting


Posted by chihyu on 2021-01-25

Hoisting

提升:寫在後面的會被裝有寫在前面

  • 變數宣告可以提升,賦值的部分沒有
  • function 可以提升
    console.log(a) // undefined
    var a = 10 
    ====
    假想的流程:
    var a // 只提升了宣告 a 的部分,後面賦值的部分不提升
    console.log(a)
    a = 10
    
    test() // function 自動被假想成已經出現在前面了
    function test() {
      console.log(10)
    }
    

順序

  • 參數的優先權大於變數宣告
  • function 的優先權大於變數宣告、參數
  • 兩個相同名稱的 function,後面定義的為優先
  • 已經宣告且賦值的變數如果再宣告但不賦值會被忽略
    function > 參數 > 變數

補充:ECMAScript 是 JS 遵照的一個標準

執行環境 Execution contexts

當 control(這個 control 不知道是甚麼) 進入一個 execution code(假想成一個個 function) 就會進入一個新的 execution contexts(EC),EC 會形成一疊(stack),最上層的 EC 是正在執行的 EC,執行完才會清掉(pop)
一開始的時候會有一個 Global execution contexts

變數初始化 variable instantiation

  • 每個 EC 都有一個 variable object(gloabal 的,function 的是 activation object),在 source text(不知道指甚麼)裡宣告的 variable 和 function 會做為 variable object 的 properties, function 的參數也會

初始化順序:

  1. 找參數 (formal parameter):如果寫入的參數比原本的參數少,多於的參數會初始化成 undefined,如果有相同的參數名稱,value 是屬於最新的參數
  2. 找 function 的宣告(FunctionDeclaration):如果 variable object 裡面發現有同名的 properties,那他的 value 跟 attirbute 就會被取代(被取代的依據是甚麼:source text order)
  3. 找 variable 的宣告(VariableDeclaration):如果有同名的,並不會影響到任何事情,如果沒有同名的,就初始化成 undefined

variable object (vo):想像成一個 js 的物件

vo: {
    a:1
    b:1
    c:undefined
}
function text (b, c) {
    var a = 1
}
text(1)

範例執行步驟:

  1. 進入 global EC (只看 global 的東西,function 裡面的東西先不看)

    global EC
    global VO:{
    }
    
  2. 先看有沒有 parameter, 沒有 parameter 再看有沒有 function,發現一個 function test()

    global VO:{
     test: function
    }
    
  3. 找完 function 找 variable

    global VO:{
     test: function 
     a: undefined // 還沒開始執行,只是先找名字
    }
    
  4. 開始執行程式碼,從 global 的第一行開始看
  5. 第一行 a 被宣告成 1
    global VO:{
     test: function 
     a: 1 
    }
    ===
    var a = 1
    
  6. text() 被呼叫,進去一個新的 EC
    test EC
    test VO:{
    }
    
  7. 先看有沒有 parameter, 沒有 parameter 再看有沒有 function,發現一個 function inner()
    test VO:{
     inner:function
    }
    
  8. 找完 function 找 variable
    test VO:{
     inner:function
     a:undefined // 找到變數 a, 先初始化成 undefined
    }
    
  9. 開始執行程式碼,從 test 裡的第一行開始看
  10. console.log('1', a),對照 test VO 裡的 a
    test VO:{
    inner:function
    a:undefined // 找到變數 a, 先初始化成 undefined
    }
    ===
    console.log('1', a) // undefined
    
  11. var a = 7,a 被宣告成 7,把 test VO 的 a 改成 7
    test VO:{
    inner:function
    a:7 // 找到變數 a, 先初始化成 undefined
    }
    ===
    var a = 7
    
  12. console.log('2', a),對照 test VO 裡的 a
    test VO:{
    inner:function
    a:7 // 找到變數 a, 先初始化成 undefined
    }
    ===
    console.log('1', a) // 7
    
  13. a++,把 test VO 裡 a 的值 + 1
    test VO {
    inner:function
    a:8
    }
    a++
    
  14. var a,因為 a 已存在,所以不會有任何變化
  15. inner() 被呼叫,進去一個新的 EC
    inner EC
    inner VO:{
    }
    
  16. 找看看有沒有 parameter、function、variable,發現都沒有
  17. 開始執行程式碼,從 inner 裡的第一行開始看
  18. console.log('3', a),對照 inner VO 裡的 a,發現沒東西再往 test VO 去找
    test VO {
    inner:function
    a:8
    }
    ===
    console.log('3', a) // 8
    
  19. a = 30,inner VO 裡面沒有 a,再往 test VO 找,找到 a,把 a 改成 30
    test VO {
    inner:function
    a:30
    }
    
  20. b = 200,inner VO 裡面沒有 b,再往 test VO 找,test VO 裡面沒有 b,往 global VO 找,發現沒有 b,直接把 b 寫入 global
    global VO:{
    test: function 
    a: 1 
    b:200
    }
    
  21. inner 執行完畢,清掉 inner VO
  22. 回到 test EC,console.log('4', a),對照 test VO 裡的 a
    test VO {
    inner:function
    a:30
    }
    ===
    console.log('4', a) // 30
    
  23. test 執行完畢,清掉 test VO
  24. 回到 global EC,console.log('5', a),對照 global VO 裡的 a
    global VO:{
    test: function 
    a: 1 
    b:200
    }
    ===
    console.log('5', a) // 1
    
  25. a = 70,把 global VO 裡的 a 改成 70
    global VO:{
    test: function 
    a: 70
    b:200
    }
    a = 70
    
  26. console.log('6', a),對照 global VO 裡的 a
    global VO:{
    test: function 
    a: 70
    b:200
    }
    consle.log('6', a) // 70
    
  27. console.log('7', b),對照 global VO 裡的 b
    global VO:{
    test: function 
    a: 70
    b:200
    }
    consle.log('7', b) // 200
    
  28. global 的東西執行完畢,清掉 global VO
  29. 結束

整體流程概要:

  • 進入 EC,先初始化 VO,依序找 parameter、function、variable
  • 接著執行程式碼,由上到下執行
  • 碰到呼叫 function,進入一個新的 EC,初始化新的 VO
  • 當這個 function 執行完畢,把這個 function 的 VO 清掉

補充:
let、const 也有 hoisting,但跟 var 的運作方式不太一樣,是在賦值之前不會被存取,如果沒有賦值就存取不會被當作 undefined,而是會出現錯誤
(TDZ) Temporal dead zone:在宣告後跟賦值前的那個區域,在這個區域裡面不能存取

let a = 10 
function test() {
console.log(a)
let a = 30
}
test()

JS 的作用域

靜態作用域 (Static scope/lexical scope):在 function 宣告時就已經決定,跟執行沒關係,Javascript 是採用此

動態作用域 (Dynamic scope):在 function 執行時才會決定


#Web #javascript #hoisting







Related Posts

Cyberpunk 風格按鈕動畫

Cyberpunk 風格按鈕動畫

【THM Walkthrough】Lateral Movement and Pivoting (1)

【THM Walkthrough】Lateral Movement and Pivoting (1)

Elevate Your Dermatology Practice with the Electric Dermatology Chair

Elevate Your Dermatology Practice with the Electric Dermatology Chair


Comments