現在進入到 YDKJSY 的第二部 Scope & Closure。
今天再次給自己一個機會理解 JS program 在 JS Engine 是如何運作的?
Kyle 藉由三個問題,讓讀者可以思考 JS 是怎麼運作
JS program 在執行之前,其實會先經過 parsing,生成 AST,轉成電腦看得懂的指令再去執行。
Kyle 認為 JS 更像是 compiled 語言(相較於 interpreted);
這邊我認同 Kyle 說的,因為要執行 JS 都需要 JS engine,不管用 chrome (V8), safari (JavaScriptCore),為了最佳化程式執行的速度,提升效能,engine 都會 parsing JS,生成 AST,一邊轉成電腦看得懂的指令並且去執行,另外一邊生成優化的代碼,優化過的代碼就會取得先前 browser 拿去執行的 codes。
compiling 主要分成三個階段:
當程式碼在 tokenizing 階段時,意味著當前的一串程式碼正被轉成有意義的記號,這些記號會作為 parsing 的 input。
譬如 const isFriday = true;
應該會變成 const
, isFriday
, =
, true
, ;
將 tokens 轉成一個巢狀的樹,構成 Abstract Syntax Tree (AST)。
像是 const isFriday = true
; 會有一個 VariableDeclaration 作為最根本的節點,其有個 identifier (value 是 isFriday
)的子節點,也有 AssignmentExpression,這個 AssignmentExpression 有一個 booleanLiteral(value 是 true
);
這階段,會將 AST 轉成電腦看得懂的指令,這個部分會因為不同程式語言、或是其他的因素而有所不同。
所以 JS engine 會執行 const isFriday = true;
的 AST,真正創建一個變數,並將 true 存進這個變數裡(當然還有 reserving memory 那些步驟)。
從三個我也經歷過的錯誤,可以佐證 JS 在執行之前會先 parsing!
var greeting = "Hello";
console.log(greeting);
greeting = ."Hi";
// SyntaxError: unexpected token .
執行這段程式碼並不會 log 出 Hello
,而是會丟出 SyntaxError: unexpected token .
。
起初會有疑問:JS 不是由上而下執行,那應該要 log Hello
才對啊?
事實是,JS engine 會先 parsing 所有的程式碼,才會開始執行。
console.log("Hello");
saySomething("Hello","Hi);
// Uncaught SyntaxError: Duplicate parameter name not
// allowed in this context
function saySomething(greeting, greeting) {
"use strict";
console.log(greeting);
}
首先,並不會 log 出 Hello
;
在嚴謹模式下,不允許重複的參數名稱,所以必須在執行之前,丟出 early errors。這也是在執行之前,會先 parsing 的證明。
function saySomething() {
var greeting = "Hello";
{
greeting = "Hi";
let greeting = "";
console.log(greeting);
}
}
saySomething();
// Uncaught ReferenceError: Cannot access 'greeting' before initialization
這邊 Kyle 舉得例子有點 confusing,根據 MDN
JavaScript Hoisting refers to the process whereby the interpreter appears to move the declaration of functions, variables or classes to the top of their scope, prior to execution of the code.
而 Kyle 這邊應該是想證明:
let
(and const
), so it throws the reference error再 saySomething 函式裡頭,有個由 {}
組成的 block,當 engine parsing 到 greeting = "Hi";
時,發現這個變數與下一行的 let greeting = "";
有同樣的 identifier,因此判斷 greeting
在宣告之前就被調用了,所以丟出 reference error,因為 greeting
太早被使用了。
let
或者const
宣告並不會 hoisted,因為 TDZ (temporary dead zone) ...嗎?在接下來第五節就會知道了!
今天份的學習到這邊