摘要:編譯原理在傳統(tǒng)編譯語(yǔ)言的流程中,程序中的一段源代碼在執(zhí)行之前會(huì)經(jīng)歷三個(gè)步驟,統(tǒng)稱為編譯。定義聲明是在編譯階段進(jìn)行的,而賦值是在執(zhí)行階段進(jìn)行的。
編譯原理
在傳統(tǒng)編譯語(yǔ)言的流程中,程序中的一段源代碼在執(zhí)行之前會(huì)經(jīng)歷三個(gè)步驟,統(tǒng)稱為“編譯”。
詞法分析
將由字符組成的字符串分解成(對(duì)編程語(yǔ)言來說)有意義的代碼塊,這些代碼塊被稱為詞法單元(token)。
語(yǔ)法分析
這個(gè)過程是將詞法單元流(數(shù)組)轉(zhuǎn)換成一個(gè)代表了程序語(yǔ)法結(jié)構(gòu)的樹。這個(gè)樹被稱為“抽象語(yǔ)法樹”(AST)。
代碼生成
將 AST 轉(zhuǎn)換為可執(zhí)行代碼的過程被稱為代碼生成。AST 轉(zhuǎn)化為一組機(jī)器指令。
JavaScript 引擎要復(fù)雜得多。例如,在語(yǔ)法分析和代碼生成階段有特定的步驟來對(duì)運(yùn)行性能進(jìn)行優(yōu)化,包括對(duì)冗余元素進(jìn)行優(yōu)化等。
與其他語(yǔ)言不同,JavaScript 的編譯過程不是發(fā)生在構(gòu)建之前的。對(duì)于 JavaScript 來說,大部分情況下編譯發(fā)生在代碼執(zhí)行前的幾微秒(甚至更短)的時(shí)間內(nèi)。
引擎:
從頭到尾負(fù)責(zé)整個(gè) JavaScript 程序的編譯及執(zhí)行過程。
編譯器:
負(fù)責(zé)語(yǔ)法分析及代碼生成等臟活累活。
作用域:
負(fù)責(zé)收集并維護(hù)由所有聲明的標(biāo)識(shí)符(變量)組成的一系列查詢,并實(shí)施一套非常嚴(yán)格的規(guī)則,確定當(dāng)前執(zhí)行的代碼對(duì)這些標(biāo)識(shí)符的訪問權(quán)限。
編譯器可以控制作用域、而引擎更多的是查詢(有可能會(huì)拋出異常)。
引擎在查詢作用域的時(shí)候,有兩種查詢方法:
LHS:當(dāng)變量出現(xiàn)在賦值操作的左側(cè)時(shí),試圖找到變量的位置。如果在頂層(全局作用域)中也無法找到目標(biāo)變量,全局作用域中就會(huì)創(chuàng)建一個(gè)具有該名稱的變量。
RHS:當(dāng)變量出現(xiàn)在賦值操作的右側(cè)時(shí),簡(jiǎn)單地查詢值。在所有嵌套的作用域中遍尋不到所需的變量,引擎就會(huì)拋出 ReferenceError 異常。
作用域有兩種主要的工作模型:詞法作用域(靜態(tài))、動(dòng)態(tài)作用域;
詞法作用域是在編譯器詞法分析的時(shí)候生成的。
eval和with會(huì)動(dòng)態(tài)改變?cè)~法作用域,這樣會(huì)影響js引擎編譯階段的優(yōu)化工作,會(huì)慢。所以嚴(yán)格模式禁用
聲明提升js的代碼在生成前,會(huì)先對(duì)代碼進(jìn)行預(yù)編譯,編譯的一部分工作就是找到所有的聲明,然后建立作用域?qū)⑵潢P(guān)聯(lián)起來,因此,在當(dāng)前作用域內(nèi)包括變量和函數(shù)在內(nèi)的所有聲明都會(huì)在任何代碼被執(zhí)行前首先被處理。
定義聲明是在編譯階段進(jìn)行的,而賦值是在執(zhí)行階段進(jìn)行的。
** var 與 let區(qū)別
編譯過程中用var定義的變量,會(huì)默認(rèn)分配undefined值;
用let定義的變量,不會(huì)有任何值分配,所以在let之前使用這個(gè)變量會(huì)報(bào)錯(cuò)。
提升的優(yōu)先級(jí):
函數(shù)提升的優(yōu)先級(jí)比變量高(函數(shù)聲明會(huì)提升到變量聲明之前,變量聲明一定會(huì)被忽略)
對(duì)于同名的變量聲明,Javascript采用的是忽略原則,后聲明的會(huì)被忽略
對(duì)于同名的函數(shù)聲明,Javascript采用的是覆蓋原則,先聲明的會(huì)被覆蓋
示例1:
console.log(c) // ReferenceError b = function c() {};
等號(hào)右邊的是不會(huì)變量提升的喲,可以見得編譯過程中沒有管等號(hào)右邊。
示例2:
b = function c() {}; console.log(c) // ReferenceError
執(zhí)行過程中創(chuàng)建了c變量,但是只能在函數(shù)c中使用
示例3:
b = function c() { c = 3; }; b(); console.log(c); // ReferenceError
等價(jià)于
b = function() { let c = 3; c = 4 // 沒有賦值到window上喲 }; b(); console.log(c); // ReferenceError
示例4
(function () { try { throw new Error(); } catch (x) { var x = 1, y = 2; // 注意這里的x被覆蓋 console.log(x); } console.log(x); console.log(y); })(); // 1 undefiend 2
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/105771.html
摘要:無論你使用的是解釋型語(yǔ)言還是編譯型語(yǔ)言,都有一個(gè)共同的部分將源代碼作為純文本解析為抽象語(yǔ)法樹的數(shù)據(jù)結(jié)構(gòu)。和抽象語(yǔ)法樹相對(duì)的是具體語(yǔ)法樹,通常稱作分析樹。這是引入字節(jié)碼緩存的原因。 這是專門探索 JavaScript 及其所構(gòu)建的組件的系列文章的第 14 篇。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! 如果你錯(cuò)過了前面的章節(jié),可以在這里找到它們: JavaS...
摘要:是詞法作用域工作模式。使用可以將變量綁定在所在的任意作用域中通常是內(nèi)部,也就是說為其聲明的變量隱式的劫持了所在的塊級(jí)作用域。 作用域與閉包 如何用js創(chuàng)建10個(gè)button標(biāo)簽,點(diǎn)擊每個(gè)按鈕時(shí)打印按鈕對(duì)應(yīng)的序號(hào)? 看到上述問題,如果你能看出來這個(gè)問題實(shí)質(zhì)上是考對(duì)作用域的理解,那么恭喜你,這篇文章你可以不用看了,說明你對(duì)作用域已經(jīng)理解的很透徹了,但是如果你看不出來這是一道考作用域的題目,...
摘要:是詞法作用域工作模式。使用可以將變量綁定在所在的任意作用域中通常是內(nèi)部,也就是說為其聲明的變量隱式的劫持了所在的塊級(jí)作用域。 作用域與閉包 如何用js創(chuàng)建10個(gè)button標(biāo)簽,點(diǎn)擊每個(gè)按鈕時(shí)打印按鈕對(duì)應(yīng)的序號(hào)? 看到上述問題,如果你能看出來這個(gè)問題實(shí)質(zhì)上是考對(duì)作用域的理解,那么恭喜你,這篇文章你可以不用看了,說明你對(duì)作用域已經(jīng)理解的很透徹了,但是如果你看不出來這是一道考作用域的題目,...
摘要:是詞法作用域工作模式。使用可以將變量綁定在所在的任意作用域中通常是內(nèi)部,也就是說為其聲明的變量隱式的劫持了所在的塊級(jí)作用域。 作用域與閉包 如何用js創(chuàng)建10個(gè)button標(biāo)簽,點(diǎn)擊每個(gè)按鈕時(shí)打印按鈕對(duì)應(yīng)的序號(hào)? 看到上述問題,如果你能看出來這個(gè)問題實(shí)質(zhì)上是考對(duì)作用域的理解,那么恭喜你,這篇文章你可以不用看了,說明你對(duì)作用域已經(jīng)理解的很透徹了,但是如果你看不出來這是一道考作用域的題目,...
閱讀 3416·2023-04-25 22:04
閱讀 2208·2021-11-22 15:29
閱讀 2180·2021-10-11 10:57
閱讀 1422·2021-09-24 09:48
閱讀 3162·2021-09-09 09:34
閱讀 2561·2021-09-02 15:21
閱讀 2406·2019-08-30 15:53
閱讀 1147·2019-08-30 14:07