You Don't Know JS: Scope & Closures (第4章: Hoisting)
Chapter4: Hoisting
變量附加到哪個層次的scope,由它們在哪裏和如何聲明(let, var)來決定。
Function scope/Block scope都有相同的法則:任何變量在一個scope內聲明,則這個變量附加到這個作用域上。
但有一個細節問題:當聲明declarations出現在一個作用域中的不同的位置的時候,scope附加如何與declarations協作?
Chicken or The Egg?
temptation: a strong desire to have or do sth even thought you know you should not(邪念,誘惑人的事物)
當程序執行時,JS 代碼被一行行,從上到下的順序被解譯。但有例外:
a = 2; var a; console.log( a ); 輸出2
可是:
console.log( a ); var a = 2;
//輸出undefined
怎麽回事? 一個先有雞還是先有蛋的問題。 the declaration是蛋, assignment是雞。
The Compiler Strikes Again 編譯器又罷工了
Engine實際是先編譯JS code,在interprtes it之前。
編譯器先聲明變量,然後Engine在Scope中查詢這個變量,如果發現就分配它。
所以思考事情的最好的方式是:
所有的聲明declarations,包括變量和函數,被首先處理processed。在你的代碼被執行executed前。
var a = 2; 其實是2個statements: var a;和a = 2;
var a 是聲明,在編譯階段被處理;
a = 2是assignment, 會留在執行階段execution phase處理。
所以,前2個例子就可以理解了:
//第一個例子: var a; a = 2; console.log(a); // 2 //第二個例子: var a; console.log(a); //undefined a = 2;
結論:先有蛋(declarations),後有?? assignment.
- 只有聲明自身會被hoisted!!
- 另外, hoisting是per-scope,只在聲明所在的作用域內hoisting它。
- 函數表達式不是聲明!!所以不會hoisting它!
-
foo(); // not ReferenceError, but TypeError! var foo = function bar() { // ... };
Uncaught TypeError: foo is not a function
解釋:
var foo被提升hoisting,但它沒有value。
使用foo(),就是認為foo的值是函數,用()執行函數。
typeof foo //undefined
所有,提示?,foo不是一個函數,不能使用()!
(表達式和聲明的區分見第三章,Function as Scope)
另一個例子:
foo(); // TypeError ,foo is not a function
bar(); // ReferenceError, (bar是函數,但是)bar is not defined
var foo = function bar() {
// ...
};
等同於:
var foo;
foo(); // TypeError
bar(); // ReferenceError
foo = function() {
var bar = ...self...
// 證明作為表達式,bar 只在內部{ .. }被使用
}
Functions First
- 先函數聲明hoisting,再變量聲明hoisting。
- 多重/副本 var聲明會被忽略。 但可以重寫函數聲明。
foo(); // 3
function foo() { //hoisting ,第一行
console.log( 1 );
}
var foo = function() { //多重var聲明,被忽略,不會hoisting。
console.log( 2 ); //這裏重寫了foo函數。
};
function foo() { //hoisting,第二行
console.log( 3 );
}
foo() //輸出2
ES7最新版:塊內函數聲明, 要小心使用!foo沒有hoisting到全局作用域!!
Review
var a = 2;其實是2部分,var a 是在編譯階段處理, =2在執行階段處理
所有聲明都首先被處理!在它們執行前!
分配, 甚至分配一個函數表達式,都不會被hoisted!
小心duplicate declarations。尤其是混合了正常的var聲明和函數聲明,要避免這種使用!
寫代碼先寫聲明,是好的習慣!!
You Don't Know JS: Scope & Closures (第4章: Hoisting)