什麼是 Hoisting
會輸出 undefined 而不是 ReferenceError: b is not defined,這種現象就叫做 Hoisting,變數的宣告被「提升」到最上面去了。
- 先來個報錯案例
console.log(b)
// b is not defined
- Hoisting (提升)
console.log(b)
var b = 10
// 印出 b 是 undefined,而非報錯
// 這個現象,稱之為 Hoisting(提升)
var b
console.log(b)
b = 10
// 我們可以想像成,把變數拉上去
// 只有宣告會提升,賦值不會
- 宣告與賦值的提升關係
test()
var test = function() {
console.log(123)
}
// test is not a function
var test // undefined
test()
test = function() {
console.log(123)
}
// 宣告會被提升,賦值不會
- 宣告 function 與 呼叫 function
test()
function test() {
console.log(123)
}
// 我們可以在宣告 function 之前,先呼叫,這也是 Hoisting
// function 整體被提升
// JS 做得到,但其他程式語言不一定
function test() {
console.log(123)
}
test()
Hoisting 的順序問題
- Hoisting 與變數有關
Hoisting 只會發生在變數的 Scope 裡面
var a = 'global'
function test() {
console.log(a)
var a = 'local'
}
test()
// 印出 undefined
var a = 'global'
function test() {
var a // 變數在這個作用域內,被提升
console.log(a)
a = 'local'
}
test()
- 來談談提升(Hoisting)的順序:變數 v.s. function
var a = 'global'
function test() {
console.log(a)
var a = 'local'
function a() {
}
}
test()
// 印出 [function:a]
===
var a = 'global'
function test() {
console.log(a)
function a() {
}
var a = 'local'
}
test()
// 更動 var a = 'local' 位置,仍會印出 [function:a]
// 因為 function 會有優先權
function test() {
function a() {
}
console.log(a)
a = 'local' // var a 可以不寫出來,因為在 function 那裡已有宣告
}
// 提升的運作可以以此為想像
// function 的優先度,會比宣告變數高
- 來談談提升(Hoisting)的順序:function v.s. function
function test() {
console.log(a)
a()
function a() {
console.log(1)
}
function a() {
console.log(2)
}
var a = 'local'
}
// 印出 [function:a], 2
// 宣告兩個同名的 function,後面宣告的 function 蓋掉 之前的 function
- 來談談提升(Hoisting)的順序:參數與變數
function test(a) {
var a // 只是宣告一個變數 a
console.log(a)
a = 456
}
test(123)
// 印出 123
function test(a) {
var a = undefined
console.log(a)
a = 456
}
test(123)
// 印出 undefined
- 來談談提升(Hoisting)的順序:參數與 function
function test(a) {
console.log(a)
function a() {
}
}
test(123)
// 印出 [function:a]
- 需要釐清的地方
function test() {
var a = 'test'
var a // 我要宣告一個變數 a
console.log(a)
}
test()
- 更進一步
function test(a) {
console.log(a)
var a = 456
console.log(a)
}
test(123)
印出 123, 456
提升(Hoisting)的結論,提升的優先順序
- function
- arguments (傳進函式的參數)
- var
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);
- 一步一步解析
- 宣告一個全域變數
- 宣告一個 function 叫 test
- 然後執行函式 test
- 執行函式 test,我們來到第三行
- 第三行要印出 a , 先看下面有沒有宣告 a
- 看到第四行和第七行,有宣告變數也有賦值 (宣告會被提升,賦值不會)
- console.log('1.', a),a 印出 undefined
- 第四行宣告 a ,並賦值 7
- console.log('2.', a),a 印出 7
- 第六行 a++,這時數字為 8
- 第七行 var a ,沒有作用,因為第四行已經有宣告 a
- 第八行,執行 inner()
- inner(),執行 console.log('3.', a)
- 會先在 inner(),有沒有宣告變數 a,沒有再往上一層找
- 接著 碰到 a = 30,並非變數宣告,往上一層找變數宣告,然後去改變 a 值
- 接下來 碰到 b = 200,global variable 也找不到的話,就會直接在宣告一個全域變數
var b
- console.log('5.', a),看到全域變數 a = 1
- console.log('6.', a),前一行改變 a 的賦值 = 70
- console.log('7.', b),b = 200
hoisting 到底是怎麼運作的?
來看看 ECMAScript:JS 界的聖經
以下摘文來自 Huli 文章:https://blog.huli.tw/2018/11/10/javascript-hoisting-and-tdz/
Execution Contexts
這邊先非常簡單介紹一下什麼是 Execution Contexts(以下簡稱 EC),每當你進入一個 function 的時候,就會產生一個 EC,裡面儲存跟這個 function 有關的一些資訊,並且把這個 EC 放到 stack 裡面,當 function 執行完以後,就會把 EC 給 pop 出來。Variable object
每個 EC 都會有相對應的 variable object(以下簡稱 VO),在裡面宣告的變數跟函式都會被加進 VO 裡面,如果是 function,那參數也會被加到 VO 裡。
參考資料:https://blog.huli.tw/2018/11/10/javascript-hoisting-and-tdz/