JavaScript - 預(yù)編譯

JavaScript 是一門腳本語言,其可以不經(jīng)過編譯而直接運(yùn)行(這點(diǎn)與 Java 不同,Java 需要編譯之后才能運(yùn)行),但是 JavaScript 存在一個(gè)預(yù)編譯的問題。

預(yù)編譯


  1. JavaScript 腳本的運(yùn)行由兩個(gè)階段組成:預(yù)編譯階段執(zhí)行階段,先進(jìn)行預(yù)編譯,再執(zhí)行語句;
  2. 預(yù)編譯,簡(jiǎn)單理解,就是在內(nèi)存中開辟一塊空間,用來存放變量和函數(shù)。在 JavaScript 中,JavaScript 會(huì)在內(nèi)存中為使用 <code>var</code> 關(guān)鍵字聲明的變量和使用 <code>function</code> 關(guān)鍵字聲明的函數(shù)開辟一塊空間,用來存放使用這兩者聲明的變量和函數(shù);
  3. 在預(yù)編譯時(shí),<code>function</code> 的優(yōu)先級(jí)比 <code>var</code> 高,如:
function a(){};
var a = 1;

此時(shí) <code>a</code> 的類型是 <code>function</code>,而不是 <code>number</code>;

  1. 注意:預(yù)編譯時(shí)并不會(huì)對(duì)變量進(jìn)行賦值(即不會(huì)進(jìn)行初始化),變量賦值是在 JavaScript 腳本執(zhí)行階段進(jìn)行的。

下面我們通過一些例子來理解預(yù)編譯。

實(shí)例1


代碼:

<script>
    console.log('before: ' + a); // ①
    var a = 1;                   // ②
    console.log('after: ' + a);  // ③
</script>

效果演示:

實(shí)例1.png

結(jié)果分析:

  1. JavaScript 腳本在執(zhí)行之前先進(jìn)行預(yù)編譯,所以 ① 和 ③ 不會(huì)執(zhí)行,而是先執(zhí)行 ②,進(jìn)行預(yù)編譯;
  2. 因?yàn)轭A(yù)編譯階段是不對(duì)變量進(jìn)行賦值的,即不進(jìn)行初始化,所以 ② 也只執(zhí)行前半部分 <code>var a;</code>,由于只聲明了 <code>a</code> 而沒有進(jìn)行賦值,所以此時(shí) <code>a</code> 的值為 <code>undefined</code>;
  3. 預(yù)編譯完畢之后,JavaScript 腳本開始執(zhí)行,執(zhí)行順序按照從上到下的順序執(zhí)行。
    所以,上面的代碼可以拆分成下面的樣子:
<script>
    console.log('before: ' + a); // ①
    var a;                       // ②
    a = 1;                       // ③
    console.log('after: ' + a);  // ④
</script>

首先進(jìn)行預(yù)編譯,先執(zhí)行 ②,之后預(yù)編譯結(jié)束,按照從上到下的順序依次執(zhí)行 ① -> ③ -> ④,執(zhí)行 ① 的時(shí)候,由于此時(shí) <code>a</code> 并未進(jìn)行賦值,其默認(rèn)的值為 <code>undefined</code>,所以輸出 <code>before: undefined</code>,之后執(zhí)行 ③,對(duì) <code>a</code> 進(jìn)行賦值,此時(shí) <code>a = 1</code>,最后執(zhí)行 ④,輸出 <code>after: 1</code>。

實(shí)例2


代碼:

<script>
    a();                        // ①
    console.log(a);             // ②
    function a(){               // ③
        console.log('function');// ④
    }                           // ⑤
    console.log(a);             // ⑥
    a();                        // ⑦
</script>

效果演示:

實(shí)例2.png

結(jié)果分析:
上面的代碼中我們聲明了一個(gè)函數(shù) <code>a()</code>,按照前面說的,在 JavaScript 腳本執(zhí)行之前會(huì)先進(jìn)行預(yù)編譯,所以函數(shù) <code>a()</code> 會(huì)被預(yù)編譯,之后再執(zhí)行 JavaScript 語句,① 為執(zhí)行函數(shù) <code>a()</code>,② 為在控制臺(tái)輸出函數(shù) <code>a()</code>。

實(shí)例3


代碼:

<script>
    a();                    // ①
    function a(){           // ②
        console.log('a');   // ③
    }                       // ④
    var a = 1;              // ⑤
    a();                    // ⑥
</script>

效果演示:

實(shí)例3.png

結(jié)果分析:

  1. 預(yù)編譯階段:②③④ 和 ⑤ 的前半部分 <code>var a;</code> 執(zhí)行,由于 <code>function</code> 優(yōu)先級(jí)比 <code>var</code> 高,所以預(yù)編譯結(jié)束之后 <code>a</code> 的類型為 <code>function</code>;
  2. 執(zhí)行階段:按順序從上往下執(zhí)行,首先執(zhí)行 ①,此時(shí) <code>a</code> 的類型為 <code>function</code>,執(zhí)行沒問題;然后把 <code>1</code> 賦值給 <code>a</code>,由于 JavaScript 是弱類型語言,此時(shí) <code>a</code> 的類型為 <code>number</code>,之后再執(zhí)行函數(shù) <code>a()</code>,會(huì)出現(xiàn)錯(cuò)誤:Uncaught TypeError: a is not a function,因?yàn)?<code>a</code> 此時(shí)本質(zhì)上是一個(gè)變量,而不是函數(shù)。

實(shí)例4


代碼:

<script>
    b();                    // ①
    var b = function () {   // ②
        console.log('b');   // ③
    };                      // ④
    b();                    // ⑤
</script>

效果演示:

實(shí)例4.png

結(jié)果分析:

  1. 預(yù)編譯階段:執(zhí)行 ② 的前半部分 <code>var b;</code>,此時(shí) <code>b</code> 為變量,其值為 <code>undefined</code>(注意:此階段并不會(huì)將匿名函數(shù)賦值給 <code>b</code>,賦值在執(zhí)行階段才進(jìn)行);
  2. 執(zhí)行階段:首先執(zhí)行 ①,由于 <code>b</code> 的類型為變量,而不是 <code>function</code>,所以會(huì)報(bào)錯(cuò):預(yù)編譯.html:9 Uncaught TypeError: b is not a function,此時(shí)程序出錯(cuò),不再往下執(zhí)行;
  3. 將 ① 屏蔽,再次運(yùn)行腳本,預(yù)編譯階段還是一樣,執(zhí)行階段:將一個(gè)匿名函數(shù)賦值給變量 <code>b</code>,此時(shí) <code>b</code> 的類型為 <code>function</code>,再執(zhí)行 <code>b()</code>,控制臺(tái)正常輸出 <code>b</code>。

實(shí)例5


代碼:

<script>
    function c() {          // ①
        console.log('c1');  // ②
    }                       // ③
    c();                    // ④
    function c() {          // ⑤
        console.log('c2');  // ⑥
    }                       // ⑦
</script>

效果演示:

實(shí)例5.png

結(jié)果分析:

  1. 預(yù)編譯階段:先執(zhí)行 ①②③ 和 ⑤⑥⑦,由于 ⑤⑥⑦ 聲明的函數(shù)與 ①②③ 聲明的函數(shù)同名,所以后聲明的函數(shù)會(huì)覆蓋掉之前聲明的函數(shù);
  2. 執(zhí)行階段:執(zhí)行 ④,由于前面所說,所以在控制臺(tái)會(huì)輸出 <code>c2</code>。

總結(jié)


總之,要理解預(yù)編譯,只要弄清兩點(diǎn):變量/函數(shù)聲明變量賦值。在預(yù)編譯階段,只進(jìn)行 變量/函數(shù)聲明,不會(huì)進(jìn)行變量的初始化(即變量賦值,所有變量的值都是 <code>undefined</code>);變量賦值 是在執(zhí)行階段才進(jìn)行的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容