首頁技術文章正文

Javascript預解析過程

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

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

我們來看下面這個例子:


func();

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


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

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

JavaScript 預解析

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

關鍵問題是怎么處理呢?

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

重新來看上面的那段代碼


func();

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

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


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

func();


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


alert(a);
var a = 1;

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

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

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

JavaScript預解析


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

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


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


復雜點的情況分析

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

函數同名

觀察下面這段代碼:

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

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

輸出結果為:

This is last func1
This is last func1

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


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

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


變量和函數同名


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

當出現變量聲明和函數同名的時候,只會對函數聲明進行提升,變量會被忽略。所以上面的代碼的輸出結果為


function foo(){}

我們還是來吧預解析之后的代碼展現出來:


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

再來看一種


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

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


Uncaught TypeErrornum is not a function

直接上預解析后的代碼:


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

預解析是分作用域的

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


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

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


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

預解析是分段的

分段,其實就分script標簽的


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

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


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

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

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

函數表達式并不會被提升


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

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

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


條件式函數聲明

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

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

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中的文字說明,條件式函數聲明的處理和函數表達式的處理方式一樣,所以條件式函數聲明沒有聲明提升的特性。


猜你喜歡:

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

如何在JavaScript中獲取當前日期?

React Hooks新手入門教程

web前端課程

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