深刻理解JS中的“預編譯階段”和“執行階段”
什麼是“預編譯”
1、javascript是一種解釋型語言,例如C、Java等強型別語言中的編譯階段,
它是沒有這個步驟的,因此,javascript中有了類似於強型別語言編譯階段的步驟---預編譯,
同時,我們需要知道,js引擎不是逐行的解釋程式碼,而是按照程式碼塊解釋,
即,以<script></script>標籤為塊,進行解釋,
另外,我們還需要知道的是,預編譯過程是在執行過程的前一刻發生並執行完畢,
也就是說到了執行階段的時候,預編譯的過程已經完成了;
2、我們在程式設計過程中有自己的一套語言和語法,同樣的,計算機也有自己的一套,
面對程式碼,我們可以清晰的知道它的執行方式和順序,但是,計算機不一定能懂,
因此,預編譯也是讓計算機“認識”我們所寫的程式碼的過程,
例如下文將會提到的,AO物件,就是JS引擎在執行程式碼過程中重要的依賴;
複製程式碼
javascript中的宣告方式
在js中宣告的方式有兩種,變數宣告和函式宣告
即,var(let、const)和function
瞭解這個對下面預編譯的過程有重要幫助
複製程式碼
下面隨著程式碼分析預編譯的過程
<script>
function test(a) {
console.log(a);
var a = 123;
console.log(a);
function a() {
};
console.log(a);
var b = function () {
};
console.log(b);
function d() {
};
}
test(1);
</script>
先自己嘗試著寫出這段程式碼執行後的結果是什麼?
複製程式碼
函式預編譯發生在函式執行的前一刻
第一步 建立AO物件
AO物件:官方翻譯為執行期上下文
在上述程式碼中,script標籤內,簡單的看,有一個函式的定義及該函式的傳參執行兩個步驟,
js引擎掃描完程式碼,在 “test(1)”這行程式碼將要執行的前一刻,開始了“預編譯”的過程,
建立js引擎所認識的AO物件,該物件中存放著變數和方法的宣告,僅供js引擎進行讀取等操作,
每個函式都會有自己的AO物件,此時這個物件是屬於test這個函式的,
也就是說這個AO物件就是test函式的作用域
AO {
}
複製程式碼
第二步 找形參和變數宣告,將變數和形參名作為AO的屬性名,值為undefined
形參為a,作為鍵新增到AO物件中,值為undefined;
變數宣告有兩個,var a;var b;
注意,由於宣告的變數a和形參相同,那麼只需要新增一次就可以了,
那麼AO物件就變為如下:
AO {
a:undefined,
b:undefined
}
複製程式碼
注意: 1、相同的變數名作為一次屬性名 2、變數宣告,不是函式宣告
第三步 將實參值和形參統一
test(1) ===> 實參為1,將1賦值給AO物件中的屬性a
AO {
a:1,
b:undefined
}
複製程式碼
第四步 在該函式體裡面找函式宣告,值為函式體
函式宣告有兩個
function a() {}
function d() {}
其中a函式宣告的函式名與變數宣告中的變數名相同,在此步驟中,函式宣告優先順序高於變數宣告,
將會覆蓋之前的變數宣告,同時,新增d的函式宣告,
此時AO物件變為:
AO {
a: function a() {},
b: undefined,
d: function d() {}
}
複製程式碼
注意: function xxx() {} 為函式宣告 var xxx = function () {} 是函式表示式,是變數宣告,不是函式宣告,
到此時,預編譯的過程完成,下面是函式執行過程
1、執行console.log(a);
此時從AO物件中可以知道,a的值為 function a() {}
故,這碼執行的結果為 fucntion a() {}
複製程式碼
2、程式碼繼續執行
var a = 123; 執行完該行程式碼之後AO物件變為
AO {
a: 123,
b: undefined,
d: function d() {}
}
故,執行的結果為 123
複製程式碼
3、繼續執行的程式碼為函式宣告
在預編譯過程的時候已將其提升,AO物件不變
故,此行程式碼執行的結果為 123
複製程式碼
4、繼續執行
變數b在預編譯過程僅僅提升宣告,並沒有對其賦值,此時對變數b進行賦值
AO物件變為:
AO {
a: 123,
b: function () {},
d: function d() {}
}
故,此行程式碼執行的結果為 function () {}
複製程式碼
5、程式碼就往下執行,這部分程式碼在預編譯過程時候處理過,至此,函式執行完畢
上述程式碼執行的結果為
function a() {}
123
123
function () {}
複製程式碼
練習題
function fn(a, b) {
console.log(a);
console.log(b);
var b = 234;
console.log(b);
a = 123;
console.log(a);
function a() {
};
var a;
var b = function () {
}
console.log(a);
console.log(b);
}
fn(1);
複製程式碼