JavaScript 預編譯過程的詳細解讀

文章目錄

JavaScript運行三部曲

JavaScript預編譯

01 關於預編譯的一些知識點

02 預編譯四部曲(局部)

1. 創建AO對象(Activation Object) (執行期上下文)

2. 找形參和變量聲明,將變量和形參名作爲AO屬性名,值爲undefined

3. 將實參值和形參統一 (全局預編譯省略這一步)

4. 在函數體裏面找函數聲明,值賦予函數體

03 綜合例題------全局和局部的預編譯和解釋執行過程

1. 全局預編譯三部曲

2. 局部預編譯四部曲

3. JavaScript的解釋執行

JavaScript運行三部曲

在學習預編譯之前,我們要先了解一下js的運行步驟:

語法分析:通篇檢查代碼是否有語法錯誤,若有,則程序不會執行;若無,則進行預編譯。

預編譯

解釋執行:從上到下依次執行函數代碼。

JavaScript預編譯

01 關於預編譯的一些知識點

(1)函數聲明整體提升:不管你的函數聲明寫在哪裏,系統總是會將其提升到邏輯最前面,所以不管你是在函數的上面或下面調用函數,其實都是在下面調用的。(2)變量僅聲明提升:如 var a = 123;則將其拆成兩個部分 即 聲明var a位置提升到邏輯最前端,賦值a=123不提升。所以若是在聲明賦值變量前進行調用,則會顯示undefined,而不會報錯。(3)imply global 暗示全局變量:即任何變量,如果變量未經聲明就賦值,此變量就爲全局對象所有。即:

function test(){

a = 10; //在局部裏面賦值,沒有進行var聲明

}

因爲沒有進行var聲明,相當於在全局域裏定義,即window.a = 10;

var a = b = 123;//局部裏

上面代碼的執行順序:先將123賦給b,其次是b的值賦給a,但是聲明只聲明瞭a,沒有聲明b,所以此時b歸window所有。

(4)一切聲明的全局變量,全是window的屬性,即歸window所有,而調用全局變量,其實就是調用window.變量。(window 就是全局的域)即:

var b = 234; //在全局域裏面聲明定義

即 相當於window.b = 234。

02 預編譯四部曲(局部)

下面就通過check這個函數來解釋預編譯的四個步驟

function check(a) {

console.log(a);

var a = 123; //變量聲明和定義

console.log(a);

function a() {} //函數聲明

console.log(a);

var b = function () {} //變量聲明和定義

console.log(b);

function d() {} //函數聲明

}

check(1);

1. 創建AO對象(Activation Object) (執行期上下文)

  AO {}

2. 找形參和變量聲明,將變量和形參名作爲AO屬性名,值爲undefined

從上面的代碼中,我們可以知道形參是a,也定義了變量b。

AO {

a : undefined,

b : undefined,

}

3. 將實參值和形參統一 (全局預編譯省略這一步)

函數執行傳入的實參是1,所以形參a=1。

AO {

a : 1,

b : undefined,

}

4. 在函數體裏面找函數聲明,值賦予函數體

a的值變成了函數體(存在同名的情況,直接覆蓋以前的值),還添加了一個d屬性,值爲函數體。

AO {

a : function a() {},

b : undefined,

d : function d() {}

}

以上預編譯過程就結束了,接下來就是js三步曲中的第三步------解釋執行。(解釋執行的時候,AO對象裏的屬性值也會發生改變,而每次想要輸出的變量,其實就是在AO對象中找對應的值)

//開始js的解釋執行,修改AO對象

function check(a) {

// 此時的AO對象就是預編譯結束之後的樣子

    // AO {

    // a : function a() {},

    // b : undefined,

    // d : function d() {}

    // }

    console.log(a); //輸出function a() {}

    var a = 123; //預編譯已經將var a提升執行過了,所以直接執行賦值a=123

    // AO {

    // a : 123,

    // b : undefined,

    // d : function d() {}

    // }


    console.log(a); //輸出123

    function a() {} //預編譯已經提升聲明瞭,被優先執行了,所以不看了

    console.log(a); //輸出123

    var b = function () {} //預編譯已經將變量b的聲明提升執行了,所以只用看賦值

    // AO {

    // a : 123,

    // b : function() {},

    // d : function d() {}

    // }


    console.log(b); //輸出function() {}


    function d() {} //預編譯已經提升聲明瞭,被優先執行了,所以不看了

}

check(1); //函數執行,實參爲1

03 綜合例題------全局和局部的預編譯和解釋執行過程

現在就通過下面的代碼來練習一下全局和局部一起的預編譯和它們的執行過程:

console.log(test);

function test(test) {

            console.log(test);

            var test = 234;

            console.log(test);

function test() { }

}

test(1);

var test = 123;

1. 全局預編譯三部曲

(1)創建GO對象:全局預編譯的情況下,會生成Global Object對象,其實和AO同理,只是換了個名字。(GO === window)

GO {}

(2)找變量聲明作爲GO屬性名,值爲undefined

GO {

test : undefined

}

(3)找函數聲明,值賦予函數體

GO {

test : function test(test) {...}

}

2. 局部預編譯四部曲

(此時的AO對象爲函數test裏的)

(1)創建AO對象

AO {}

(2)找形參和變量聲明賦值undefined

AO {

test : undefined

}

(3)實參形參統一

AO {

test: 1

}

(4)找函數聲明,值賦予函數體

AO {

test: function test() {}

}

123

3. JavaScript的解釋執行

// js開始解釋執行

//全局

// GO {

// test : function test(test) {...}

// }

console.log(test); // function test(test) {...}

function test(test) {

// 局部

// AO {

// test: function test() {}

// }

console.log(test); // function test(){}

var test = 234;

// AO {

// test: 234

// }

console.log(test); // 234

function test() {}//預編譯已經提升了聲明,被優先執行了,所以不看了

}

test(1);

var test = 123;

// GO {

// test : 123

// }

console.log(test);  //123

// js執行結束

注意:如果GO中和AO中都有test函數,若在局部中就先找AO的,但如果AO沒有的話再找GO;若在全局中就找GO的用。(就近找,自己沒有找爸爸,爸爸沒有找爺爺等)

個人筆記,歡迎大家交流探討!

百度網盤鏈接:https://pan.baidu.com/s/1vF1A7beP0oovDaUFvjY9MA 提取碼: r5d3

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章