Q: 什麼是 Node.js ?
A:Node.js 是指運於 web 服務端的 JavaScript,基於 Chrome V8 引擎,有非阻塞,事件驅動 I/O 等特性。
Q: 等等,你剛提到了 Chrome V8 引擎,它是什麼,為什麼使用它而不是其它引擎?
A:JavaScript 引擎是執行 JavaScript 程式碼的程式或直譯器,JavaScript 引擎可以實現為標準直譯器,或者以某種形式將 JavaScript 編譯為位元組碼的即時編譯器。它的工作流程大致如下:

Chrome V8 引擎便是其中一種,由 Google 開發,使用 C++ 編寫,它的工作流程幾乎與上圖一致:

相比於其它 JavaScript 引擎轉換成位元組碼或者解釋執行,V8 將 JavaScript 程式碼轉換成更高效的機器碼(IA-32, x86-64, ARM, or MIPS CPUs)。它通過 JIT(Just-In-Time)編譯器實現,不生成位元組碼或任何中間程式碼。並且使用瞭如 Inlining、Shapes、Inline Caches 等方法來提高效能。
Q:很好,現在我已經瞭解一點 Chrome V8 引擎有什麼用了,但是你列舉的那些方法,真讓我頭大。
A:比如 Shapes 與 Inline Caches 用來優化物件屬性載入。
Q:嗯?
A:ECMAScript 規範基本上將所有物件定義為由字串鍵值對映到 property 屬性的字典,其中 [[]]
雙方括號是規範定義不能直接暴露給 JavaScript 的屬性的表示方法。

Q:在記憶體中也是這麼儲存?
A:不不不,如果在記憶體中這麼儲存,那就浪費空間了。比如說有相同形狀的物件 object = { x: 7, y: 8 }
,它們的屬性名是相同的,並且在屬性值的完整字典中,也只有 [[value]]
不同。
Q:那應該分開儲存,把除 [[value]]
之外的所有屬性名和其餘特性單獨儲存。並且它需要有一個屬性,來告知 JavaScript 引擎去哪查詢具體的值。
A:是的,引擎將物件的 Shape
分開儲存,如下 JSObject 只是儲存 [[value]]
,Shape
中有一個 Offset
偏移量來告知 JavaScript 取哪找具體的值:

當有多個具有相同形狀物件時,優勢變得清晰可見。因為只需要將它們的形狀與鍵值屬性資訊儲存一次!

Q:原來是這樣,不過那和 Inline Caches 有什麼關係?
A:關係大了,Shapes 主要是用來實現 Inline Caches(ICs)的,Inline Caches 是 JavaScript 快速執行的關鍵因素之一。
Q:

A:比如有一個從物件中獲取 x 屬性的函式,在 JSC(JavaScriptCore) 中執行時,會生成以下位元組碼:

Inline Caches 在第一個指令 get_by_id
指令中,由兩個未初始化的插槽組成。
當呼叫函式 getX({ x: 'a' })
時,像前面所說,物件 { x: 'a' }
有一個包含屬性 x
的 Shape,該 Shape 包含屬性 x 的偏移量和其它特性,當第一次執行該函式時,會把該屬性的 Shape 和 偏移量儲存在 Inline Caches 中:

後續呼叫該函式時,Inline Caches 只需要對比 Shape,如果與以前相同,則只需要從偏移量載入該屬性值。這比每次查詢要快很多。
Q:很精彩!
A:V8 所做的,遠不止這些,在即將要釋出的 7.2 版本中,解析時間明顯降低,縮短載入時間,提高響應速度:

Q:好了,我們們不說 V8 了,我對你前面提到的非阻塞和事件驅動 I/O 挺感興趣的。
A:......
未完待續