努力求學沒有得到別的好處,只不過是愈來愈發覺自己的無知
寫在前面
最近在讀一本書《你不知道的JavaScript(上)》
,剛好讀到了提升這個章節。聲明提升早在之前的公開課上就有所瞭解,不過那時候第一次接觸還不是很清楚。當再次讀到它的時候彷彿故友重逢一般,從中得到了更加深刻的認識,然後就做一下筆記(個人理解)。
然後這本書寫的還是蠻好的,推薦大家有時間讀一讀,作者講的比較風趣,而且通俗易懂。
聲明提升
劃一下重點
- 聲明提升在預編譯階段
- 函數,變量的聲明都會被提升,且函數會提到變量前面
- 變量提升是不會超出當前作用域的
- 被提升的只有聲明哦,其他語句還是在原來的位置
變量聲明提升
{
a=5;
var a;
console.log(a); //5
}
如果按照從上到下理解的思路,a的聲明在賦值後面,a會被重新賦值爲undefined
但實際上並不是這樣,在編譯階段a的聲明語句會被提升
,上面的代碼其實和下面的代碼是等價的
{
var a;
a=5;
console.log(a); //5
}
注意:只有聲明會被提升,其他語句是保留在原地的,看看下面的代碼:
{
console.log(a); //undefined
var a = 2;
}
按照上面的聲明提升,有人可能會認爲是ReferenceError,但實際結果是undefined,爲什麼呢?
上面的代碼和下面的代碼是等價的:
{
var a;
console.log(a); //undefined
a = 2;
}
就像之前說的,提升的只是變量的聲明,var a = 2;
其實是分兩部分的
var a; //聲明
a = 2; //賦值(執行階段)
預編譯四部曲
先了解下預編譯,理解了預編譯,可能對於後面的提升會有更好理解
- 創建AO對象(執行期上下文)
- 找到形參和變量聲明,並將形參名和變量名作爲AO對象的屬性,默認值undefined
- 實參與形參統一
- 在函數體中找到函數聲明,賦值
函數聲明提升
函數聲明提升和變量是類似的,不過函數聲明會提到變量的聲明之前
,看下面的例子:
{
var a = 2;
console.log(a); //2
function a() {
a = 6;
}
a(); //TypeError
}
事實上,上面的代碼是有問題的,a是沒有辦法作爲函數執行的,上面的代碼可以看作這樣:
{
//編譯
var a = undefined;
a = function a(){
a = 6;
}
//執行
a = 2;
console.log(a); //2
a(); //TypeError
}
可以看到,編譯階段首先聲明a,然後給a賦值爲function(){},之後開始執行,但是在一開始a
就a=2
被覆蓋了,所以a不再是一個函數
。
下面是一個我曾經做過的練習,是一個綜合的例子:
function fn(a){
console.log(a);
var a = 123;
console.log(a);
function a(){}
console.log(a);
console.log(b);
var b = function(){}
console.log(b);
}
fn(1);
- 關於這個例子我要說明一點,在fn()內部 ,傳參發生在最前面,這一點我還不曉得爲啥子。
先不着急看結果,先一步步探討代碼編譯、執行過程,上面的代碼經過預編譯之後:
var fn;
fn = function(a){
//編譯
var a = undefined;
var b = undefined;
a = 1; //傳進來的參數
a = function a(){} //a函數聲明提升
//執行
console.log(a);
a = 123;
console.log(a);
//function a(){}
console.log(a);
console.log(b);
b = function(){} //這裏是函數表達式,只會提升變量b的聲明,賦值會保留
console.log(b);
}
fn(1);
所以結果是:
function a(){}
123
123
undefined
function (){}
或許一開始接觸可能會有些頭皮發麻,其實只要能夠清楚變量和函數提升的規則,理解起來就比較容易了!