首頁技術(shù)文章正文

Javascript中的預(yù)解析是怎么實現(xiàn)的?

更新時間:2020-08-05 來源:黑馬程序員 瀏覽量:

JavaScript是解釋型的語言,但是他并不是真的在運行的時候逐句的往下解析執(zhí)行。

我們來看下面這個例子:


func();

function func(){
    alert("Funciton has been called");
}


在上面這段代碼中,函數(shù)func的調(diào)用是在其聲明之前,如果說JavaScript代碼真的是逐句的解析執(zhí)行,那么在第一句調(diào)用的時候就會出錯,然而事實并非如此,上面的代碼可以正常執(zhí)行,并且alert出來`Function has been called`。

所以,可以得出結(jié)論,JavaScript并非僅在運行時簡簡單單的逐句解析執(zhí)行!

JavaScript 預(yù)解析

JavaScript引擎在對JavaScript代碼進行解釋執(zhí)行之前,會對JavaScript代碼進行預(yù)解析,在預(yù)解析階段,會將以關(guān)鍵字`var`和`function`開頭的語句塊提前進行處理。

關(guān)鍵問題是怎么處理呢?

當(dāng)變量和函數(shù)的聲明處在作用域比較靠后的位置的時候,變量和函數(shù)的聲明會被提升到作用域的開頭。

重新來看上面的那段代碼


func();

function func(){
    alert("Funciton has been called");
}


由于JavaScript的預(yù)解析機制,上面的代碼就等效于:


function func(){
    alert("Funciton has been called");
}

func();


看完函數(shù)聲明的提升,再來看一個變量聲明提升的例子:


alert(a);
var a = 1;


由于JavaScript的預(yù)解析機制,上面這段代碼,alert出來的值是undefined,如果沒有預(yù)解析,代碼應(yīng)該會直接報錯a is not defined,而不是輸出值。

Wait a minute, 不是說要提前的嗎?那不是應(yīng)該alert出來1,為什么是undefined?

那么在這里有必要說一下聲明、定義、初始化的區(qū)別。其實這幾個概念是C系語言的人應(yīng)該都比較了解的。

JavaScript預(yù)解析


所以我們說的提升,是聲明的提升。

那么再回過頭看,上面的代碼就等效于:


var a//這里是聲明
alert(a);//變量聲明之后并未有初始化和賦值操作,所以這里是 undefined
a = 1;


復(fù)雜點的情況分析

通過上一小節(jié)的內(nèi)容,我們對變量、函數(shù)聲明提升已經(jīng)有了一個最基本的理解。那么接下來,我們就來分析一些略復(fù)雜的情況。

函數(shù)同名

觀察下面這段代碼:

func1();
function func1(){
    console.log('This is func1');
}

func1();
function func1(){
    console.log('This is last func1');
}


輸出結(jié)果為:

This is last func1
This is last func1


原因分析:由于預(yù)解析機制,func1的聲明會被提升,提升之后的代碼為:


function func1(){
    console.log('This is func1');
}
function func1(){
    console.log('This is last func1');
}
func1();
func1();

同名的函數(shù),后面的會覆蓋前面的,所以兩次輸出結(jié)果都是`This is last func1`。


變量和函數(shù)同名


alert(foo); 
function foo(){}
var foo = 2;

當(dāng)出現(xiàn)變量聲明和函數(shù)同名的時候,只會對函數(shù)聲明進行提升,變量會被忽略。所以上面的代碼的輸出結(jié)果為


function foo(){}


我們還是來吧預(yù)解析之后的代碼展現(xiàn)出來:


function foo(){};
alert(foo);
foo = 2;


再來看一種


var num = 1;
function num () {
    alertnum );
}
num();


代碼執(zhí)行結(jié)果為:


Uncaught TypeErrornum is not a function


直接上預(yù)解析后的代碼:


function num(){
    alert(num);
}
num = 1;
num();


預(yù)解析是分作用域的

聲明提升并不是將所有的聲明都提升到window對象下面,提升原則是提升到變量運行的環(huán)境(作用域)中去。


function showMsg() 
    var msg = 'This is message'
alert(msg); // msg未定義


還是直接把預(yù)解析之后的代碼寫出來:


function showMsg() 
    var msg;
    msg = 'This is message'
alert(msg); // msg未定義

預(yù)解析是分段的

分段,其實就分script標(biāo)簽的


<script>
func();  // 輸出 AA2;
function func(){
    console.log('AA1');
}

function func(){
    console.log('AA2');
}
</script>


<script>
function func(){
    console.log('AA3');
}
</script>


在上面代碼中,第一個script標(biāo)簽中的兩個`func`進行了提升,第二個`func`覆蓋了第一個`func`,但是第二個script標(biāo)簽中的`func`并沒有覆蓋上面的第二個`func`。所以說預(yù)解析是分段的。

tip:但是要注意,分段只是單純的針對函數(shù),變量并不會分段預(yù)解析。

函數(shù)表達(dá)式并不會被提升


func();
var func = function(){
    alert("我被提升了");
};


這里會直接報錯,`func is not a function`,原因就是函數(shù)表達(dá)式,并不會被提升。只是簡單地當(dāng)做變量聲明進行了處理,如下:

var func;
func();
func = function(){
    alert("我被提升了");
}


條件式函數(shù)聲明

上面這段代碼,就是所謂的條件式函數(shù)聲明,這段代碼在Gecko引擎中打印`"undefined"`、`"function"`;而在其他瀏覽器中則打印`"function"`、`"function"`。

原因在于Gecko加入了ECMAScript以外的一個feature:條件式函數(shù)聲明。

Conditionally created functions Functions can be conditionally declared, that is, a function declaration can be nested within an if statement.

Note: Although this kind of function looks like a function declaration, it is actually an expression (or statement), since it is nested within another statement. See differences between function declarations and function expressions.

Note中的文字說明,條件式函數(shù)聲明的處理和函數(shù)表達(dá)式的處理方式一樣,所以條件式函數(shù)聲明沒有聲明提升的特性。

猜你喜歡:

Js中深拷貝與淺拷貝的區(qū)別

如何在JavaScript中獲取當(dāng)前日期?

React Hooks新手入門教程

web前端課程

分享到:
在線咨詢 我要報名
和我們在線交談!