跳至主要内容

JavaScript 基礎

Javascript Engine

  • JavaScript 語法是透過 JavaScript Engine 轉換為二進位訊號讓電腦理解
  • V8 引擎於 2008 年由 Google 發布,目的是提升 JavaScript 在瀏覽器的效能。Google Map 也得以在 Chrome 瀏覽器順暢運行。V8 發布前後,也有許多引擎問世,其蓬勃發展促使 JavaScript 各方面效能愈好。

Exercise: Javascript Engine

  • JavaScript 的主要創造者 Brendan Eich 也開發了最早的 JavaScript 引擎。
    在此之前,瀏覽器無法讀取 JavaScript,只能讀取 HTML 與 CSS。

Inside the Engine

  • 下列連結可將 JavaScript 代碼轉換為 AST(抽象語法樹 Abstract Syntax Tree)
    AST Explorer

Interpreters and Compilers

V8 引擎內建直譯器與編譯器,所以 JavaScript 可以兩種形式執行。

Inside the V8 Engine

  • interpreter(直譯器)
    直譯,不做任何優化,所以雖然起步快,但整體速度較慢。
    例如,若碰上迴圈等重複性質高的程式碼,速度會變慢。
  • compiler(編譯器)
    總覽後再翻譯,會優化程式碼,所以雖然起步慢,但整體速度較快。

為了解決直譯器、編譯器各自的缺點,JIT Compiler 即時編譯器應運而生,瀏覽器速度大幅提昇。

JavaScript Engine.png

上圖解說:

  1. JavaScript 程式碼進入 V8 引擎後,先由 Parser 剖析器轉換為 AST 抽象語法樹。
  2. 抽象語法樹進入直譯器,逐行轉換為 Bytecode。
  3. 抽象語法樹進入直譯器後,也會立刻進入 Profiler 及 Compiler,產出 Optimized Code。
  4. 起步快但速率慢的 Bytecode 與 起步慢但速率快的 Optimized Code 不斷傳遞給電腦,程式得以執行。

JIT 編譯器並非萬能,品質差的程式碼甚至會使編譯器效能降低,導致「反優化」現象。
工程師若能理解以上示意圖的原理,有助於提高編碼品質的意識。

Comparing Other Languages

JavaScript、Java 都是直譯語言,經過各自的直譯器(JavaScript machine or Java virtual machine)直譯成 Bytecode 後可執行,但電腦無法直接執行,因為電腦只懂機械語言,不懂 Bytecode。

C++ 是編譯語言,可直接轉換為機械語言。

問:JavaScript 是直譯語言嗎? 答:視執行環境而定。如果執行環境僅有編譯器,則 JavaScript 就是編譯語言。

Writing Optimized Code

不利於編譯器的語法、導致反優化的語法

  • eval( )
  • arguments
  • for in
  • with
  • delete

較難理解的概念

  • Hidden classes
  • Inline caching

結論:編碼順序雜亂、難以預測的程式碼不只令人困惑,也會降低編譯器的效率,導致程式碼反優化。

WebAssembly

問:Why not just use machine code from the beginning?

答:因為機械語言起步速度較慢,會影響使用者體驗。回顧 1995 年,瀏覽器技術不如今日成熟,
發行商又要角逐市佔率,使用者體驗至關重要。今日則有新技術 WA(WebAssembly)
此技術可能在未來改變遊戲規則。

維基百科 WebAssembly

Call Stack and Memory Heap

  • Memory Heap 堆積記憶體
    JavaScript Engine 藉此分配記憶體、儲存變數、程式碼等等

  • Call Stack 堆疊記憶體
    JavaScript Engine 藉此追蹤程式碼執行到哪一行

在巢狀結構中,Call Stack 有先進後出的概念。
在瀏覽器中,第一個執行的一定是 Global Execution Context,
所以在其後調用的函式至少會是位於全域執行上下文的巢狀結構內。
第一個進入 Call Stack 的必然是全域執行上下文,
最後一個離開 Call Stack 的也必然是全域執行上下文。

變數的值若是基本型別,就儲存在 Call Stack;若是物件型別就儲存在 Memory Heap。

在 Chrome Console 觀察以下代碼
// Memory Heap
const number = 10; // 將記憶體分配給 number
const string = "some text"; // 將記憶體分配給 string
const human = {
first: "Andrei",
last: "Neagoie",
}; // 將記憶體分配給物件及其值

// Call Stack
function subtractTwo(num) {
return num - 2;
}

function calculate() {
const sumTotal = 4 + 5;
return subtractTwo(sumTotal);
}

debugger;
calculate();

Stack Overflow

以下代碼利用 Recursion 無限次巢狀調用函式,造成 Stack Overflow。

function inception() {
inception();
}

inception();

Memory Leaks

以下是容易導致 Memory Leak 的編碼習慣

// Global Variable:宣告太多全域變數
var a = 1;
var b = 1;
var c = 1;

/*
Event Listeners:宣告事件監聽器,卻不在觸發事件後移除事件監聽器。
倘若使用者頻繁往返同一頁面,事件監聽器將持續增加。
*/
var element = document.getElementById("button");
element.addEventListener("click", onClick);

// setInterval
setInterval(() => {
// referencing objects...
});

Single Threaded

JavaScript 只有一個 Call Stack,是單執行緒語言。
1995 年 JavaScript 誕生之初只需處理單純的 HTML,所以單執行緒就夠用。
單執行緒語言猶如單手進食,只有一隻手能拿食物放入口中,只有口中咀嚼吞嚥食物後才能再放入新的食物。

Exercise: Issue With Single Thread

單執行緒 syncronous 語言的缺點:一次只能執行一件事,
導致整個程式有過多等待時間或中途停滯不前。
alert 語法即是一例,當 alert 視窗彈出,除非按下確定鍵,程式會一直停在 alert。

JavaScript Runtime

JavaScript 單執行緒語言的特性雖然不便,但仍廣受歡迎,
是因為實務上有 Runtime 的配合,使得 JavaScript 有許多彈性。

JS Runtime.jpg

Web API 由瀏覽器提供,讓 JavaScript 可在 Runtime 之中執行 async 處理。
Runtime 的功能:發送 HTTP 請求、監聽 DOM 事件、setTimeout 等。
在開發者工具的 console 檢視 window 物件,即可檢視 Runtime 提供了什麼功能(Web API);
該功能皆非 JavaScript 原生語法。
瀏覽器係以 C++ 等低階語言提供執行環境及許多語法。

// 試參考上圖解釋程式流程
console.log("1");
setTimeout(() => {
console.log("2"), 0;
});
console.log("3");
  1. Event Loop 持續觀察 Callback Queue 與 Call Stack 是否閒置
  2. console.log('1') 進入 Call Stack
  3. 主控台顯示 1
  4. console.log('1') 離開 Call Stack
  5. setTimeout 進入 Call Stack
  6. JavaScript 將 setTimeout 交給 Web API
  7. console.log(’3') 進入 Call Stack;Web API 同時處理 setTimeout,
    等待零秒後將 setTimeout 丟進 Callback Queue
  8. console.log(’3') 離開 Call Stack;Event Loop 持續詢問 Call Stack 可否接受 console.log(’2’)
  9. Call Stack 已清空,且程式碼已經執行完最後一行,所以同意 Event Loop 傳遞 console.log(’2’)
  10. console.log(’2') 進入 Call Stack
  11. console.log('2') 離開 Call Stack
  12. 所有程式執行完畢

Node.js

Node.js 是 JavaScript 的執行環境,可實現非同步、非阻塞處理。
JavaScript 原本只能在瀏覽器執行,Node.js 問世後則可在伺服器執行。
Node.js 係以 libuv(C 語言 Library)及 V8 引擎(C++)兩大技術建構。