js 運行代碼的時候分為幾個步驟:語法分析 ==》預編譯 ==》解釋執行 語法解析:通篇掃描代碼,查看語法是否出錯 解釋執行:讀一行 - 解釋一行 - 執行一行 預編譯執行的操作: // 假設之前並沒有定義a console.log(a); 列印結果:Uncaught ReferenceError: ...
js 運行代碼的時候分為幾個步驟:語法分析 ==》預編譯 ==》解釋執行
語法解析:通篇掃描代碼,查看語法是否出錯
解釋執行:讀一行 - 解釋一行 - 執行一行
預編譯執行的操作:
// 假設之前並沒有定義a console.log(a);
列印結果:Uncaught ReferenceError: a is not defined
這個大家應該都知道:在變數未定義時就對變數進行訪問就會報錯(typeof 列外)
再看一個例子:
1 // 假設之前並沒有定義變數a 2 console.log(a); 3 4 var a = 123; 5 6 console.log(a);
看上面代碼會列印什麼?會不會報錯?
列印結果:
undefined
123
為什麼這個沒有報錯:因為在預編譯的時候對變數進行了提升即變數提升。 定義的變數的聲明(var a ;)被提到了代碼塊的最前面,變數的賦值操作(a = 123)沒有變化。
所以被編譯後就相當於
1 // 假設之前沒有定義變數a 2 var a; 3 4 console.log(a); 5 6 a = 123; 7 8 console.log(a);
看下麵這個例子:
1 // 假設之前沒有定義test 2 console.log(test); 3 4 var test = 123; 5 6 function test () { 7 8 } 9 10 console.log(test);
列印結果:
function test () {}
123
為什麼第二行的輸出的不是undefined 了?執行步驟是什麼樣子的?
因為函數聲明在編譯的時候也會進行提升,叫做函數提升。
執行步驟:
1、變數提升:var test; 提升到最前面。此時test = undefined;
2、函數提升:function test () {}; 提升到最前面並覆蓋var test; 的聲明。所以第二行列印function test () {}; 此時test = function test () {}
3、進行賦值操作 test = 123; 此時test = 123;
1 function test (test) { 2 console.log(test); // function test(){var a = 789} 3 var test = 123; 4 function test () { 5 var a = 789; 6 }; 7 console.log(test) ; // 123 8 } 9 10 test(456);
Why?
變數提升和函數提升是如何實現的?
預編譯執行步驟:
(a) 函數內
1、在代碼定義之後執行之前生成活動對象AO(Activation Object ) 空對象{}
2、查找函數體內的形參和變數,定義為AO的屬性,賦值為undefined;
3、將實參跟形參統一,即把實參賦值給AO[形參]
4、查找函數里的函數聲明,將函數聲明賦值給AO 即 AO[函數名] = 函數
(b) 全局
1、在代碼定義之後執行之前生成全局環境對象GO(Global variable Object ) 空對象{}
2、查找變數,定義為GO的屬性,賦值為undefined;
3、查找函數聲明,將函數聲明賦值給GO 即 GO[函數名] = 函數;
以上面的例子分析預編譯過程:
function test (test) { console.log(test); // function test(){var a = 789} var test = 123; function test () { var a = 789; }; console.log(test) ; // 123 } test(456); var a = 111;
/** 全局 **/ // 第一步:創建GO // GO{}; // 第二步:查找變數定義為GO的屬性,賦值為undefined;
// GO{
// a: undefined
// };
// 第三步:將函數聲明賦值給GO
// GO{
// a: undefined,
// test: function test(test) {...}
// }
/** function test(test){...} 函數內 **/
// 第一步: 創建AO
// AO {}
// 第二步: 查找形參和變數,定義為AO的屬性, 賦值為undefined;
// AO {
// test: undefined
// }
// 第三步: 形參和實參統一
// AO {
// test: 456
// }
// 第四步:將函數聲明給AO
// AO {
// test: function test() { var a = 789 }
// }
之後就是一步一步的執行代碼了
function test (test) { console.log(test); // function test(){var a = 789} var test = 123; function test () { var a = 789; }; console.log(test) ; // 123 } test(456); var a = 111; // test(456); 進入到function test (test) {...} 內部 // console.log(test); // 列印 function test(){var a = 789} // var test = 123; 由於 test 變數的聲明已經提升,所以這句只執行 test = 123; 即AO[test] = 123 // AO { // test: 123 // } // function test() {var a = 789} 已經提升 // console.log(test) //列印 123 // 退出function test (test) {...} 銷毀AO // var a = 111; 由於 a 變數已經提升,這句只執行賦值 a = 111; 即 GO[a] = 111; // GO{ // a: 111, // test: function test(test) {...} // } // 當頁面銷毀時銷毀GO