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