iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 19
0
a = 2;
var a;
console.log( a );

這應該會回傳 undefined 吧!
NO,是 2。

console.log( a );
var a = 2;

這樣呢?哼哼,這應該是 2 吧,照邏輯應該是這樣。
NO,是 undefined。

編譯器再次出擊

var a = 2; 其實是兩個述句組成。(先找出位置,再放進去。)
var a;a = 2;
宣告部分,會在編譯的過程處理。
指定部分,會留在原處等執行時處理。

所以第一個問題,重新改寫

var a; // 編譯先處理
a = 2; // 執行時處理
console.log( a ); // 2

第二個問題,重新改寫

var a;
console.log( a ); // undefined
a = 2;

這種把 var a; 提高的隱喻,大家叫他拉升 ( hoisting )。

###宣告 會先於 指定。

範例:

foo();
function foo(){
    console.log( a );
    var a = 2;
}

照邏輯改寫

function foo() {
    var a;
    console.log( a );
    a = 2;
}
foo();

foo() 函式宣告和 var a 被拉升。
再執行 foo(),得到 undefined。

PS. 拉升要看範疇,所以 var a 不會拉到全域。

另一個例子。關於函式運算式 ( function expressions )

foo();
var foo = function bar(){
    console.log("haha");
};

會變成。

var foo;
foo();    // Uncaught TypeError
foo = function bar() {
    console.log("haha");
}
  1. foo 宣告被拉升至範疇最頂端。
  2. 執行 foo,因為有宣告,不會跳 ReferenceError。
foo; // undefined
  1. 但是因為要用 foo() 調用 undefined,就會是 TypeError 的非法作業。

那關於 bar() 的部分呢?

bar(); // ReferenceError

在全域,他並不存在。

var foo = function bar(){
    console.log("haha");
    bar; // function bar();
}

但在內部他已經被宣告過了!

所以完整的改寫可以這樣做。

var foo;
foo(); // TypeError
bar(); // ReferenceError
foo = function() {
    var bar = ...self...
    console.log("haha");
}

函式優先

目前為止,函數宣告和變數宣告都會被拉升。

但萬一! 函數命名和變數命名相同。

foo();
var foo;
function foo() {    // 函數宣告
    console.log( 1 );
}
foo = function(){  // 變數宣告
    console.log( 2 ); 
}

這時候印出來的會是 1。

改寫後的邏輯是

function foo() {    // hoisting
    console.log( 1 );
}
foo(); // 1
foo = function(){
    console.log( 2 );
}

注意 var foo; 是重複宣告,會忽略。因為函數宣告先於變數宣告。

但如果再次使用 foo(),則會印出 2。因為變數有了新的指定。

foo(); // 2

所以,雖然多重宣告會被忽略,但後續的函式宣告會覆寫之前的宣告。

foo(); // 3
function foo(){
    console.log(1);
}

var foo = function bar(){ // 怎樣都輪不到他,函數宣告會大於變數宣告
    console.log(2);
}

function foo(){ // 後面的會蓋住前面的
    console.log(3);
}

最後一個範例,也是很神奇。

foo(); // "b"
if ( true ){
    function foo(){ console.log("a"); }
}
else {
    function foo(){ console.log("b"); }
}

這要表達的是,函式宣告會拉升到包含他的範疇 ( enclosing scope )

所以可以看作是

function foo(){ console.log("b"); }
foo();
if (true ){..}
else {..}

參考資料

你所不知道的JS


上一篇
Day18 - 區塊範疇
下一篇
Day20 - 範疇與 Closure
系列文
你為什麼不問問神奇 JavaScript 呢?30

尚未有邦友留言

立即登入留言