談談 JavaScript 中的 宣告提前(hoisting)
有許多同學知道js在執行的時候,是從上到下,從左到右,一行一行執行的,但是不知道在這之前還要做一些事情,js程式在正式執行之前,會將所有var 宣告的變數和function宣告的函式,預讀到所在作用域的頂部,但是對var 宣告只是將宣告提前,賦值仍然保留在原位置,function 宣告,會將函式名稱和函式體都提前,而且先預宣告變數再預定義函式。這個過程也被叫做,“預解析”或者“預編譯”。
舉例:
console.log(a); //不會出錯,會輸出undefined
var a=100;
console.log(a); //100;
由於宣告提前,所以程式碼會變成這樣
var a; //宣告提前
console.log(a);//undefined
a=100; //賦值任然留在原位置
console.log(a);//100
- 注意1:
宣告提前僅能將宣告提前到所在作用域的頂部
function fn(){
console.log(a); //undefined
var a=100;
console.log(a); //100
};
fn();
console.log(a);// 報引用錯誤!
上面的程式碼 其實會變成這樣
function fn(){
var a; //僅僅提前到函式頂部
console.log(a); //undefined
a=100 ;
console.log(a); //100
};
fn();
console.log(a); //報引用錯誤
- 注意2:
函式宣告提前不同於var 變數宣告提前,使用函式宣告語句,函式名稱和函式體均會被提前,也就是說可以在宣告一個JavaScript函式之前呼叫它。
舉個例子:
console.log(fn()); //2
function fn(){
return 2;
}
練習題1
var a=123;
function a(){ return 1 }
console.log(a);
解析1:
這道題在弄明白什麼是 “宣告提前”後比較簡單做!
按照剛才講到的概念,這道題會變成這樣,
var a;
function a(){ return 1 }
a=123;
console.log(a);
所以最後會輸出 123
練習題2
function a(){ return 1 }
var a;
a();
解析2
完成這道題,還需要知道一件事情,如果未在var宣告語句中給變數指定初始值,那麼雖然宣告這個變數,但在給它存入一個值之前,它的初始值就是undefined,但是多次宣告同一變數無所謂!!!所以這道題的結果是 1程式碼會預編譯為:
var a; //初始值為 undefined
function a(){ return 1 }
a();
練習題3
function a(){ return 1 }
var a=undefined;
a();
解析3
這道題,和第2題非常的相似,只需要明白這裡 var a=undefined; 和 var a;是不同的,一個是宣告變數同時進行賦值操作,只是賦的值是undefined,一個是單純的宣告變數。
程式碼會預編譯為:
var a; //初始值為 undefined
function a(){ return 1 }
a=undefined;
a();
所以最後的結果會報錯 a is not a function
練習題4
if(!("a"in window)) {
var a = 1;
};
var a;
alert(a);
解析4:
首先說一句,在瀏覽器中,var宣告的全域性變數是屬於window物件的屬性。也就是說可以用 . 或者[]顯示出來(window.變數名 或者 window[“變數名”])。
in 運算子 是判斷物件是否為陣列/物件的元素/屬性:
格式:(變數 in 物件)
注意:
當“物件”為陣列時,“變數”指的是陣列的“索引”;
當“物件”為物件時,“變數”指的是物件的“屬性”;
這道題也就是再說,如果 window裡沒有屬性a,就宣告一個變數a,然後賦值為1,最後彈出一個警告框顯示a,當我們把這些概念弄清楚,會發現這道題其實是這樣的,
var a;
if (!("a" in window)) {
a = 1;
};
alert(a);
這樣看,我們能很清楚的看明白,在執行if語句之前,是已經聲明瞭變數a的,它的初始值是undefined,所以window裡是有屬性a的,那麼if語句執行的條件就不滿足,無法對變數a進行賦值,所以最後也會彈出undefined