跳至主要内容

[CH2] Node != JavaScript

本筆記非原創筆記,內容整理自 Advanced Node.js @ PluralSight。

Node's Architecture: V8 and libuv

node -v; // 查看 node 版本

V8 的功能可以分成 shipping, staged, 和 In Progress

node -p 'process.versions.v8'  // 查看使用的 v8 版本
node --harmony -p "<command>" // 可以使用 staged 的功能
node --v8-options | less // 可以看到所有的 v8 options

img

  • V8: 在 Node 中使用透過 V8 的 C++ API 使用 Chrome V8 引擎。
  • Core Modules: Node 也提供我們可以直接使用 JavaScript 的 API,透過它我們可以處理檔案、網際網路等等。
  • C++ Bindings: Node API 最終會透過 V8 的物件和函式樣版來執行 C++ 的程式碼。
  • libuv: Node 用來處理非同步事件的函式庫(主要是 C 語言)。
  • http-parser: 用來處理 HTTP 訊息的 C 函式庫。
  • c-ares: 執行非同步的 DNS queries。
  • OpenSSL
  • zlib: 處理非同步和串流的壓縮與解壓縮。

Node's ClI and REPL

Node Command

在 terminal 中輸入 node 即可進入 REPL(Read, Eval, Print, Loop)。

$ node      # 進入 REPL
$ node --help | less # 顯示 node 可用的 options

img

$ node -p "<script>|<file>"      # 執行 script 並列印
$ node -c

Node REPL

AutoComplete

在 REPL 中可以經常使用 TAB 鍵就可以查看這個物件底下有哪些方法或屬性可以使用(autocomplete):

$ let arr = []
$ arr.<TAB> # 列出所有 array 可用的方法
$ arr.s<TAB> # 列出所有 array 中開頭為 s 的方法
$ arr.sh<TAB> # 自動輸入 arr.shift

img

. 是在 REPL 中可以使用的關鍵字:

$ .<TAB>
$ .help<ENTER>

img

$ .exit            # 離開 REPL
$ .save session.js # 儲存 REPL 的訊息
$ .load hello.js # 將 JS 檔載入 REPL 中
$ .editor # 進入 editor mode,如果要輸入多行可用
process.argv        # 取的使用者輸入 node 後的參數

Global Object, Process, and Buffer

Global Object

在 Node 中唯一一個真正的全域物件稱作 global

Process

提供 Node application 和 Runtime Environment 之間的橋樑。它是 EventEmitter 的實例,因此我們可以從 process 發出事件(Emit Event)或監聽事件。

node -p "process" | less      # 查看 process 物件
node -p "process.versions" # 查看版本資訊
node -p "process.versions.v8"
node -p "process.env" # 回傳使用者環境資訊的影本(copy)

process.env

提供的是環境資訊的影本,因此如果我們使用 process.env.user = 'Aaron',並無法修改到 ENV.USER 的內容。

stdin 用來 read,stdout 用來 write,stderr 用來撰寫 error,這些都是預先建立好的 stream,因此無法關閉它。

event

exit 事件
  • 當 Node 的 event loop 沒有其他事情要處理時會觸發,或者手動執行 process.exit() 時觸發。
  • do one final synchronous operation before the node process terminates
process.on('exit', callback<code>)
uncaughtException 事件
  • 預設的情況下,錯誤發生時 Node 會列印 trace stack 給 stderr 並跳離程式,但如果我們增添 uncaughtException 這個事件,則會覆蓋掉原本的預設值,
process.on('uncaughtException, callback<err>');

Process @ Node.js

程式範例

// process 是 EventEmitter 的實例

/**
* [Exit]
* 當 Node 要跳離程式(結束)時會執行這段
* 但只能放同步處理的代碼
**/
process.on('exit', (code) => {
console.log(`About to exit with code ${code}`);
});

/**
* [uncaughtException]
* 當錯誤發生時要執行的指令
* 但這會覆蓋到預設的錯誤處理行為
**/
process.on('uncaughtException', (err) => {
console.error('Caught Error: ', err);

/**
* 記得要在錯誤處理的最後讓 Node 跳離程式
* 否則可能在後續導致更大的問題
**/
process.exit(1);
});

process.stdin.resume(); // keep the event loop busy
console.dog(); // 故意打錯字引發 typeError

Buffer

Buffer 這個 class 也是 global object 中可以被存取得到,在 Node 中)當我們需要讀取從 TCP stream 來的圖檔或壓縮檔,或者時其他形式的二元資料(binary data)時 Buffer 特別有用。

Buffer class 所產生的實例和由數值組成的陣列很類似,但它是 fixed-sized,且座落在 V8 heap 外的記憶體,同時我們可以把資料放在裡面,這些資料是根據他們字母(character)的長度被處理,而我們需要指定它這些字母被編碼(character encoding)的方式。

當我們從檔案或 sockets 讀取內容時,如果沒有指定編碼形式,則會拿回一個 Buffer 物件。一旦 buffer 被重組後(allocated)就沒有辦法在 resized。

Array 還有 String 一樣,Buffer 也有 include, indexOf, slice 等等一些方法,但在實際的使用上稍有不同。舉例來說,當我們對 buffers 進行 slice 時,被 slice 的 buffer 和原本的 buffer 共享相同的記憶體。

主要有三種建立 Buffer 的方式

Buffer.alloc(size[, fill, [encoding]])

透過 Buffer.alloc(size[, fill[, encoding]]) 會根據只的 size 回傳一個填滿的(filled)Buffer 實例,這個方法會明顯比 Buffer.allocUnsafe(size) 來得慢,但可以確保這個建立的 Buffer 實例絕不會包含舊的或潛在的敏感資料。

Buffer.alloc(10, 'uft-8'); // <Buffer 75 66 74 2d 38 75 66 74 2d 38>

Buffer.allocUnsafe(size)

Buffer.allocUnsafe(size)Buffer.allocUnsafeSlow(size) 都會根據特定的 size 回傳新的 Buffer,且和 Buffer.alloc 相比具有更好的效能,但在建立實例的時候不會把它們填滿(filled)因此他們的內容可能會包含舊的或敏感的資料,必須立即透過 buf.fill(0) 或撰寫它來初始化。

此外,Buffer 實例的 size 小於等於 Buffer.poolSize 時,透過 Buffer.allocUnsafe() 取得的實例可能會被共享在內部的記憶體庫(may be allocated off a shared internal memory pool);而透過 Buffer.allocUnsafeSlow() 則絕不會使用共享的內部記憶體。

Buffer.allocUnsafe(10).fill(3); // <Buffer 03 03 03 03 03 03 03 03 03 03>

Buffer.from()

Buffer.from() 可以接收許多不同類型的參數:

const string = 'touché';
const buffer = Buffer.from('touché');

console.log('string', string, string.length); // string touché 6
console.log('buffer', buffer, buffer.length); // buffer <Buffer 74 6f 75 63 68 c3 a9> 7

Buffer @ Node.js

參考資料