【筆記】JavaScript 的 Hoisting 作用

Hoisting,常見翻譯是提升或是拉升,這是學習撰寫 JavaScript 程式時相當重要且需要注意的一個直譯語言特性。不了解此特性可能造成閱讀 JavaScript 程式時有所誤解,因此將說明與範例筆記下來。

Hoisting 的作用是,在程式片段中宣告變數以及函數時,JavaScript 會在執行階段,在記憶體中將宣告的變數與函數「移動」到程式片段頂端,

但是在你所撰寫的程式片段依然保持在你所鍵入的位置。因此,若未注意到此一特性,可能會導致程式在預期之外的結果。


以下是一些實際案例展示:

function show(p1, p2) {
    return 'x: ' + p1 + ', y: ' + p2;
}

console.log(show('Value X', 'Value Y')); // 結果會是: x: Value X, y: Value Y

這是通常思考上,所想的程式由上而下運行順序。但如果我們將 cosole.log(show(x, y)); 寫到程式頂端會發生甚麼事呢?

console.log(show('Value X', 'Value Y')); // 結果還是: x: Value X, y: Value Y

function show(p1, p2) {
    return 'x: ' + p1 + ', y: ' + p2;
}

即便是先執行 show 這個函數,但仍然可以得到一樣的結果。這是因為 show 這個函數,被提升到程式片段的頂端處理,之後才執行 cosole.log(show(x, y));

同樣的宣告變數也會被提升。現在把上面的程式碼稍作修改,把給 show 的 x 和 y 另外宣告成變數:

var x = 'Value X';
var y = 'Value Y';

function show(p1, p2) {
    return 'x: ' + p1 + ', y: ' + p2;
}

console.log(show(x, y)); // 結果會是: x: Value X, y: Value Y

一樣在一般思考的執行順序下,結果會如同預想一樣,顯示出 x 與 y 的內容。現在同樣的把 show 這個函數挪到最頂層,再執行看看結果:

console.log(show(x, y)); // 結果會是: x: undefined, y: undefined

var x = 'Value X';
var y = 'Value Y';

function show(p1, p2) {
    return 'x: ' + p1 + ', y: ' + p2;
}

這樣的狀況下,程式不會出現錯誤,但得到結果變成了 undefined。因為提升的關係,其實這段程式碼在執行時,已經變成如同下方程式碼片段:

var x;
var y;

function show(p1, p2) {
    return 'x: ' + p1 + ', y: ' + p2;
}

console.log(show(x, y)); // 結果會是: x: undefined, y: undefined

x = 'Value X';
y = 'Value Y';

變成 x 與 y 都沒有給予其初始值,僅有宣告變數,所以得到的結果會是 undefined。

另外,Hoisting 除了在全域範圍(scope)會發生作用之外,於 function { } 的範圍裡也會有作用:

function show() {
    var x = 'Value X';
    var y = 'Value Y';

    console.log('x: ' + x + ', y: ' + y); // 結果會是:x: Value X, y: Value Y
}

show();

將之前的程式改寫成上方的程式片段,執行後可以正確顯示 show 這個函數裡面所宣告的 x 與 y 之值。

同之前的操作範例,若將 console.log 移到最上方執行則:

function show() {
    console.log('x: ' + x + ', y: ' + y); // 結果會是:x: undefined, y: undefined

    var x = 'Value X';
    var y = 'Value Y';
}

show();

執行 show 得到的結果也會是 undefined。

後記補充:

  • 於 for 迴圈裡的 var i 也是會受到提升的影響。
  • 在 ES6 之後可使用 let 取代 var 來限制變數的作用是在當前的 scope 之中。