淺談JavaScript 宣告提升
1 引例及基本原理
在學習JavaScript宣告提升之前,我們先看下面這個例子:
console.log(a); var a=2;
執行結果會是什麼?你可能會有以下的猜測:
1.報錯ReferenceError: a is not defined;
2.列印2;
3.列印undefined。
正確的結果是第三種,列印undefined。
下面讓我們來看看具體的原因。其實,對於var a=2;這條語句,JavaScript會將其視為兩個宣告:
- 定義宣告var a,會在編譯階段進行;
- 賦值宣告a=2,會留在原地等待執行階段進行。
而所謂的宣告提升:就是JavaScript會把var變數宣告和函式宣告都會被提升到各自作用域的頂部,而賦值操作並不會被提升。
var a; console.log(a); a=2;
我們還需要知道,不僅僅是var變數宣告會提升,函式宣告同樣也會提升,現在來看下面這個例子
foo(); function foo(){ console.log(a); var a=2; }
這個例子的執行結果為:列印undefined。這段程式碼實際上會被理解為為下面的形式:
function foo(){ var a; console.log(a); a=2; } foo();
2 關於宣告提升的常見問題
2.1 函式表示式
先看一個函式表示式的例子:
console.log(foo); var foo=function(){}
上面程式碼的執行結果為:列印undefined。實際上,變數識別符號foo被提升了,但它的賦值操作並沒有被提升,我們可以理解為下面的形式:
var foo; console.log(foo); foo=function(){}
結論:函式宣告會被提升,但函式表示式不會被提升。
2.2 宣告的優先順序
如果在同一個作用域內,存在同名的函式宣告和var變數宣告,那麼會發生什麼樣的情況呢?我們同樣再來看一個例子:
function a(){} var a; console.log(a);
var a; function a(){} console.log(a);
上面的兩種寫法,執行結果均為列印a(){}。也就是說,如果在同一個作用域內,存在同名的函式宣告和var變數宣告,則函式宣告的優先順序更高。
3 練習題
3.1 第一題
var getName = function() { console.log(1); } function getName() { console.log(2); } getName();
答案:列印1
解析:提升後的順序如下
var getName;//與函式宣告同名,故失效 function getName() { console.log(2); } getName = function() {//賦值 console.log(1); }; getName();
3.2 第二題
var a = 1; function b(){ a = 10; return; function a(){ console.log(a); } } b(); console.log(a);
答案:列印1
解析:首先,我們需要梳理清楚宣告的提升。
本題有幾個關鍵點,我們需要明白:
函式b內的a函式雖然在return之後,但它並沒有失效,它會發生宣告提升,從而提升到b函式作用域的頂部。
很多童鞋(比如我TAT)可能會錯誤地認為這題的答案是10,認為我們在最後呼叫了b函式,修改了全域性變數a。實際上,由於函式a發生了宣告提升,導致在函式b內“遮蔽”了全域性作用域中的變數a,因此,a=10;其實是將函式a重新賦值。為了進一步測驗,大家可以把a函式註釋掉,會發現此時的答案就變成了10。
注:本題還涉及到了閉包的相關知識,這一題詳細的解析請見參考資料[2],講得非常詳細。
var a;//全域性變數a發生了宣告提升 function b(){ function a(){//函式a發生宣告提升 console.log(a); } a = 10;//因為函式a離它最近,因此賦值給函式a return; } a = 1;//賦值給了全域性變數a b(); console.log(a);
4 參考資料
[1] 《你不知道的JavaScript》
[2] js中變數名與函式名重名的問題,Charles_Tian
[3] 函式宣告與變數宣告的提升機制優先順序問題,一個菜鳥的奮鬥史
以上就是淺談JavaScript 宣告提升的詳細內容,更多關於JavaScript 宣告提升的資料請關注我們其它相關文章!