前幾日,和朋友解說javascript預解析機制的時候,給了下面的範例程式碼:
var a = 1;
function a(){
alert(1111);
};
alert(a);
毫無疑問,結果應該是彈出1,簡單的說,瀏覽器在執行JS代碼時,第一步先掃描碼塊,遇到var關鍵字,則提前至當前範圍的程式碼的開頭,接著掃描function聲明,接在var之後,然後執行代碼,於是上述代碼真正執行時應是這樣的:
var a ;
function a(){
alert(1111);
};
a = 1;
alert(a);
如果有疑問的同學可以參考ECMA文檔,下面這個連結我網上搜的,不想去求證,只想理解機制的同學可以參考下:http://www.zhufengpeixun.com/jishujiangtang/javascriptjiangtang/2011-01-16/512.html
第一部分的代碼,在FF、chrome、IE下運行都是顯示1,沒啥疑問,而我朋友在FireBug console下啟動並執行時候,確彈出函數代碼,也就是:
function a(){
alert(1111);
};
隱約記得半年前在網上看到一個老外的文章說FireBug console是用eval執行輸入的代碼的,不過具體忘記了,最近去搜也沒搜到。在這裡個人推測下,FireBug是按語句一條一條eval的, 也就是上面代碼變為:
eval('var a = 1');
eval('function a(){alert(1111);}');
eval('alert(a)');
而eval是將執行的語句加入到最近範圍中,按上述的方式,每條語句都是立即執行,自然就是彈出函數代碼了。自此,FireBug的解析問題也許有一個推測的答案,希望有更權威的文獻論證。
-------------------------------------------------華麗分割線-----------------------------------------------------------------
接著是FireFox下的Bug,同樣是類似的代碼,今天不知腦抽怎麼的,居然會用這樣的代碼去測試,居然會測試出FF和IE8以及Chrome的不同執行結果,先不廢話,代碼如下:
{
var a = 1;
function a(){
alert(1111);
}
alert(a);
}
是的,你沒看錯,只是在最開始的代碼上加入了兩個大括弧而已,JS是函數級範圍,以上從文法角度說,依然是在全域下運行,依然是應該彈出1,在Chrome14和IE8下運行也是如此,可是在FireFox6下運行,居然彈出的是函數代碼,坑爹啊是不,彈出的是函數代碼啊。。 蛋都碎了。。 這又是個什麼情況?
推測1: 難道FireFox的JS引擎對花括弧內的語句是一條一條解析的,而沒有經過預解析過程?以下代碼似乎推翻了這個推測:
if(0){
var a = 1;
function a(){
alert(1111);
}
alert(a);
}
alert(a)
上述代碼在FF下,彈出的是undefined,說明a還是被預解析了,如果沒有預解析,應該是報錯的。
上述代碼在chrome和IE8下,彈出的是函數代碼,說明什麼了? 不是太理解? 再加句,執行以下代碼:
if(0){
var a = 1;
function a(){
alert(1111);
}
alert(a);
}
alert(a)
a();
以上代碼在FF下執行,先彈出undefined,接著報錯,提示a() is not a function;在IE8和Chrome下運行,彈出函數代碼,以及1111,說明a()這個語句正常執行了。說明Chrome和IE8都掃描了var和function聲明,但是因為if中的條件為假,所以a=1不會被執行,最終a是個function。 莫非FF把花括弧內的函式宣告當作函數運算式?這樣的話確實不會預解析了。
於是有了推測2:
FF對JS語句的var關鍵字和其他瀏覽器一樣,都是優先掃描,但是對語句塊(花括弧)內的function聲明有著不同的處理,IE8和Chrome對語句塊內的function聲明同樣是預解析處理, 而FF對語句塊內的function聲明則當作函數運算式,即上述代碼中的函式宣告在FF中翻譯為:var a = function(){alert(1111);}; 這樣做似乎可以對JS進行最佳化,比如上述的if語句,條件為假,內部代碼理應不被執行,而在IE8和Chrome下確發現a居然是個函數,對未理解JS預解析機制的同學會造成莫大的迷惑。
至此,似乎FireFox下的Bug也有了一個似乎合理的解釋。不過同樣的,這兩個解釋都是推測,還是希望有權威的資料出來。
摘自 Exodia的專欄