編譯器理論
編譯三大步驟
- Tokenizing (語法基本單元化) 與 Lexing (語彙分析)將一串字元拆解成有意義的組塊 (Tokens),例如將
var a = 2;
拆解為 var、a、=、2、; - Parsing (剖析)將 Token 組成樹狀結構 (AST,Abstract Syntax Tree,抽象語法樹)。
- Code-Generation (產生目的程式碼)將 AST 轉為可執行的程式碼,並做程式的最佳化。
了解範疇
卡司
- Engine 負責整個編譯過程以及程式碼執行。
- Compiler 做點髒活,負責處理編譯的三個步驟。
- Scope 負責收集與維護已宣告的變數,並規範對於目前正在執行的程式碼來說,是否可以取用。
前後往返
var a = 2;
- 編譯時:Compiler 會拆解並解析成 AST,但在產生程式碼時,會先詢問 Scope 是否已存在 a 這個變數,若沒有,則請它新增一個。
- 執行時:Engine 會詢問 Scope 是否有 a 可以取用,如果沒有會依序往外找,最後若沒有找到,就會大叫出錯了。
Compiler 說話了
查找變數可以分為兩類:
- LHS:找到變數,作為指定的目標 (e.g.
a = 2
)。 - RHS:取回一個值 (e.g.
console.log(a)
中的 a 與 console 本身)。
小測驗
function foo(a) { var b = a; return a + b; } var c = foo(2);
LHS:
- c =
- 隱含的 a = 2
- b =
RHS:
- foo
- = a
- a
- b
巢狀範疇
範疇可以鑲嵌。在目前執行的範疇找不到變數的時候,Engine 會往外層搜尋,直到找到,或是達到全域為止。
在瀏覽器中,全域就是 window 物件。window 是世界的初始 (無誤)。
錯誤
LHS 與 RHS 查找會帶來不同的錯誤。
ReferenceError:
- RHS 查找變數失敗時。
- LHS 查找失敗時,在嚴格模式 (Strict Mode) 下會拋出 ReferenceError,否則,會在全域範圍中自動新增變數。
TypeError:
- RHS:將不是函式的變數當成函式來宣告,或是在 null 或 undefined 的變數下參考一個屬性 (e.g.
var a; a.b
)。 - LHS:重複宣告 const 變數。
複習
- 解釋 Scope 是什麼。
- 解釋 LHS 與 RHS。
- 描述引擎與編譯器在遇到
var a = 2
時,會做哪些動作。 - LHS 與 RHS 查找時如果找不到會發生什麼事?
- 描述查找失敗時可能產生的錯誤。