1. 程式人生 > >You Don't Know JS: Scope & Closures (第4章: Hoisting)

You Don't Know JS: Scope & Closures (第4章: Hoisting)

amp chapter sco 另一個 ref sse apt ron erro

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.

  1. 只有聲明自身會被hoisted!!
  2. 另外, hoisting是per-scope,只在聲明所在的作用域內hoisting它。
  3. 函數表達式不是聲明!!所以不會hoisting它!
  1. 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)