什麼是Hoisting(提升)?
console.log(b) //b is not defined -> 報錯
//沒有宣告b的情況下,會直接報錯。
console.log(b)
var b = 10  //undefined
//在後面做宣告時沒有報錯?
//是因為宣告被Hoisting(提升),但賦值不會所以回傳undefined而不是10
//可以想成是先宣告再賦值,像下面這樣:
var b
console.log(b)
b = 10
用在function也很常見,讓程式碼function位置不用都 一定 要在呼叫上面。
可以先做呼叫,提高方便性。
test()
function test() {
  console.log(123)   //123
}
但用變數來宣告function又會報錯
test()
var test = function() {
  console.log(123)   //test is not a function -> 報錯
}
//這樣寫反而會報錯,因為只有宣告會提升
//可以想成是先宣告再賦值,像下面這樣:
var test
test()
test = function() {
  console.log(123)
}
function test() {
  console.log(a)
  var a = "local"
    function a() {
    }
}
test() //[Function: a]
//function會優先於var,所以回傳Function: a
//可以想成下面這樣:
function test() {
    function a() {
    }
  console.log(a)
  a = "local"
}
test() //[Function: a],如果同時有兩個function a()後面會蓋掉前面的。
function test(a) {
  console.log(a) //[Function: a]
  var a = 456
  function a() {
  }
}
test(123)
//傳入參數也不會印出123或456,會以function a為優先。
優先性順序:1.function -> 2.arguments -> 3.var
用小練習來理解scope chain和hoisting(執行順序在最下面):
var a = 1;
function test(){
  console.log("1.", a);
  var a = 7;
  console.log("2.", a); 
  a++;
  var a;
  inner();
  console.log("4.", a);
  function inner(){
    console.log("3.", a);
    a = 30;
    b = 200;
  }
}
test();
console.log("5.", a);
a = 70;
console.log("6.", a);
console.log("7.", b);
從 ECMAScript 來看 hoisting 的原理(規則)
規範沒有提到Hoisting但有講到Execution Contexts和Variable Instantiation,從這兩個來看。
Execution Contexts
在執行的時候functuon會依執行順序疊加在global上面,最上面的會先執行,執行完就消失,隨著程式碼會反覆堆高與減少,直到最底下的global執行完也等同整個程式執行完。如圖示:
圖片來自於影片與Huli文章,該文章也是引用自freecodecamp
Variable Instantiation
Every execution context has associated with it a variable object.
把它想成每個function會有一個object來存變數
function test() {
  var a = 1
}
VariableObject {
  a: 1
}
//VariableObject{}不是真實寫出來的,只是一個想成有這樣的位置存放function的值
當傳入值的時候,會幫參數初始化。
  VariableObject {
    a: 123        // 只有傳一個參數a:123
    b: undefined  // b初始值會undefined
  }
  function test(a, b) {
  }
  test(123)  
當test內有function a 存在時傳進去的參數123會優先被取代
VariableObject {
  a: 123  
  b: undefined //b從undefined變成function
}
function test(a, b) {
  function b() {
  }
}
test(123)
對變數的宣告,重覆的值不會被改變,沒有的才會被初始成undefined
VariableObject {
  a: 123        //已經取得參數值,不會因為function b裡的var a=10而被改變
  b: function   //已經是function b,不會因為function b裡的var b=20而被改變
  c: undefined  //原本就沒有的才會被放進去。並給初始值undefined
}
function test(a, b) {
  function b() {
    var a = 10
    var b = 20
    var c = 30
  }
}
test(123)
後面沒有再節錄規範原文,是直接看影片範例來理解。更詳細說明可以看參考資料Huli文章。
今天先講前半段,明天再繼續。
小題目解答先簡單放執行順序,明天再從執行環境的角度來重新理解。