通俗易懂的JS聲明提升

努力求學沒有得到別的好處,只不過是愈來愈發覺自己的無知


寫在前面

最近在讀一本書《你不知道的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(){},之後開始執行,但是在一開始aa=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 (){}

或許一開始接觸可能會有些頭皮發麻,其實只要能夠清楚變量和函數提升的規則,理解起來就比較容易了!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章