iT邦幫忙

2022 iThome 鐵人賽

DAY 19
0
自我挑戰組

I don't know JS yet系列 第 19

[ Day 19 ] I don't know JS yet - Compiling Codes

  • 分享至 

  • xImage
  •  

現在進入到 YDKJSY 的第二部 Scope & Closure
今天再次給自己一個機會理解 JS program 在 JS Engine 是如何運作的?

Kyle 藉由三個問題,讓讀者可以思考 JS 是怎麼運作

  1. How does JS know which variable are accessible by any given statement?
  2. How does JS handle two variables with the same name?
  3. Hos JS engine process our programs before it runs ?

Compiling Codes (in engines)

JS program 在執行之前,其實會先經過 parsing,生成 AST,轉成電腦看得懂的指令再去執行。

Kyle 認為 JS 更像是 compiled 語言(相較於 interpreted);
這邊我認同 Kyle 說的,因為要執行 JS 都需要 JS engine,不管用 chrome (V8), safari (JavaScriptCore),為了最佳化程式執行的速度,提升效能,engine 都會 parsing JS,生成 AST,一邊轉成電腦看得懂的指令並且去執行,另外一邊生成優化的代碼,優化過的代碼就會取得先前 browser 拿去執行的 codes。

compiling 主要分成三個階段:

  1. Tokenizing / Lexing
  2. Parsing
  3. Code Generation

Tokenizing / Lexing

當程式碼在 tokenizing 階段時,意味著當前的一串程式碼正被轉成有意義的記號,這些記號會作為 parsing 的 input。
譬如 const isFriday = true; 應該會變成 const, isFriday, =, true, ;

Parsing

將 tokens 轉成一個巢狀的樹,構成 Abstract Syntax Tree (AST)。
像是 const isFriday = true; 會有一個 VariableDeclaration 作為最根本的節點,其有個 identifier (value 是 isFriday)的子節點,也有 AssignmentExpression,這個 AssignmentExpression 有一個 booleanLiteral(value 是 true);

Code Generation

這階段,會將 AST 轉成電腦看得懂的指令,這個部分會因為不同程式語言、或是其他的因素而有所不同。
所以 JS engine 會執行 const isFriday = true; 的 AST,真正創建一個變數,並將 true 存進這個變數裡(當然還有 reserving memory 那些步驟)。

先 parsing 再 execution

從三個我也經歷過的錯誤,可以佐證 JS 在執行之前會先 parsing!

Syntax Errors

    var greeting = "Hello";

    console.log(greeting);

    greeting = ."Hi";
    // SyntaxError: unexpected token .

執行這段程式碼並不會 log 出 Hello,而是會丟出 SyntaxError: unexpected token .
起初會有疑問:JS 不是由上而下執行,那應該要 log Hello 才對啊?
事實是,JS engine 會先 parsing 所有的程式碼,才會開始執行。

Early Errors

    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 的證明。

Hoisting

    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 這邊應該是想證明:

  • how hoisting not works on let (and const), so it throws the reference error
  • engine will parse entire codes and throw errors before execution

再 saySomething 函式裡頭,有個由 {} 組成的 block,當 engine parsing 到 greeting = "Hi"; 時,發現這個變數與下一行的 let greeting = ""; 有同樣的 identifier,因此判斷 greeting 在宣告之前就被調用了,所以丟出 reference error,因為 greeting 太早被使用了。

let 或者 const 宣告並不會 hoisted,因為 TDZ (temporary dead zone) ...嗎?在接下來第五節就會知道了!

今天份的學習到這邊


上一篇
[ Day 18 ] I don't know JS yet - prototype
下一篇
[ Day 20 ] I don't know JS yet - variables & scope ( in compilation)
系列文
I don't know JS yet30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言