[JavaScriptWeird]No.57 hoisting 基本觀念

前言

hoisting (提升) 也是常常被拿出來詢問的觀念,所以我決定要好好地記錄下來,拆成多篇記錄,由淺入深。

hoisting (提升)

使用一連串的範例來了解 hoisting 做了些什麼:

1
console.log(b); // b is not defined

因為辦法使用未宣告的變數。

但如果改成這樣子寫,就不會出錯了。

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

這裡就是 hoisting 的一個特性,可以想像成它把所有的宣告提升到第一行,也就是說這段程式碼與下列這段程式碼的結果是一樣的:

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

但必須注意的是,hoisting 並不是真的物理性的把程式碼給拉到第一行。

從這邊可以知道一些 hoisting 的特性:

  • 變數宣告會被提升,賦值不會

除了變數之外,其實函式也具有 hoisting 特性,但在介紹之前,先簡單複習一下函式陳述式 (Function Statements) 與函式表示式 (Function Expressions),或者點一下[JavaScriptWeird]No.26 函式表示式與函式陳述句複習。

函式表示式與函式陳述式

1
2
3
function greet() {  
console.log('Hi');
}

上面是函式陳述式,下面是函式表示式

1
2
3
var anonymousGreet = function greet() {  
console.log('Hi');
}

這邊只要知道這樣就可以了,接著來看範例。

1
2
3
4
test();  
function test(){
console.log('Hello');
}

這個範例的 hoisting 就算是 JavaScript 的初學者一定也遇過,因為 hoisting 的關係,所以允許把函式寫在呼叫該函式的下方,這在某些程式語言是做不到的,而我們也可以把這段程式想像成這樣,結果是一樣的:

1
2
3
4
function test(){  
console.log('Hello');
}
test();

但如果函式寫成表示式的話,就沒有提升的效果了。

1
2
3
4
test(); // test is not a function  
var test = function(){
console.log('Hello');
}

其實原因很簡單,因為我們一開始看的範例已經得知:

只有變數宣告會被提升,賦值不會

也就是說這段程式碼可以看成這樣:

1
2
3
4
5
var test; // undefined  
test();
test = function(){
console.log('Hello');
}

由於變數 test 的值是 undefined ,並不是函式所以無法被呼叫。

對現況再做個總整理:

  • 變數宣告會被提升,賦值不會
  • 函式陳述式會被提升,函式表示式不會 (因為提升的是變數宣告)

最後再來個陷阱題

1
2
3
4
5
6
var a = 'global';  
function test(){
console.log(a); // undefined
var a = 'local';
}
test();

可能會很直覺的回答變數 a 會印出 global ,因為 test 函式內並沒有看到變數 a

但這邊事實上會印出 undefined ,因為 hoisting 會發生在變數的 scope 裡面,也就是說我們可以想像成這樣:

1
2
3
4
5
6
7
var a = 'global';  
function test(){
var a;
console.log(a); // undefined
a = 'local';
}
test();

可…可惡,陷阱真多!

後記

關於 hoisting 的基礎觀念大概是這樣,但其實 hoisting 背後還有相當深的觀念需要了解,特別推薦 Huli 大大的作品 - 我知道你懂 hoisting,可是你了解到多深?

0%