1. 程式人生 > >Node.js從入門到實戰(二)Node.js基本用法

Node.js從入門到實戰(二)Node.js基本用法

參考:

一、Node.js中的模組

Node.js使用require引入依賴的模組,因此模組是Node.js中的重要組成部分,這篇部落格主要羅列一下常用的Node.js模組,並且在後期會新增在工作中用到的模組參考備用。

二、Node.js EventEmitter

Node.js 所有的非同步 I/O 操作在完成時都會發送一個事件到事件佇列。Node.js裡面的許多物件都會分發事件,所有這些產生事件的物件都是 events.EventEmitter 的例項。

events 模組只提供了一個物件: events.EventEmitter。EventEmitter 的核心就是事件觸發與事件監聽器功能的封裝。你可以通過require("events");來訪問該模組。

// 引入 events 模組
var events = require('events');
// 建立 eventEmitter 物件
var eventEmitter = new events.EventEmitter();
EventEmitter 的每個事件由一個事件名和若干個引數組成,事件名是一個字串,通常表達一定的語義。對於每個事件,EventEmitter 支援 若干個事件監聽器。當事件觸發時,註冊到這個事件的事件監聽器被依次呼叫,事件引數作為回撥函式引數傳遞。
//event.js 檔案
var events = require('events'); 
var emitter = new events.EventEmitter(); 
emitter.on('someEvent', function(arg1, arg2) { 
    console.log('listener1', arg1, arg2); 
}); 
emitter.on('someEvent', function(arg1, arg2) { 
    console.log('listener2', arg1, arg2); 
}); 
emitter.emit('someEvent', 'arg1 引數', 'arg2 引數'); 
emitter 為事件 someEvent 註冊了兩個事件監聽器,然後觸發了 someEvent 事件。執行結果中可以看到兩個事件監聽器回撥函式被先後呼叫。 這就是EventEmitter最簡單的用法。EventEmitter 提供了多個屬性,如 on 和 emit。on 函式用於繫結事件函式,emit 屬性用於觸發一個事件。接下來我們來具體看下 EventEmitter 的屬性介紹
序號 方法 & 描述
1 addListener(event, listener)
為指定事件新增一個監聽器到監聽器陣列的尾部。
2 on(event, listener)
為指定事件註冊一個監聽器,接受一個字串 event 和一個回撥函式。
server
.on('connection',function(stream){ console.log('someone connected!');});
3 once(event, listener)
為指定事件註冊一個單次監聽器,即 監聽器最多隻會觸發一次,觸發後立刻解除該監聽器。
server.once('connection',function(stream){
  console.log('Ah, we have our first user!');});
4 removeListener(event, listener)

移除指定事件的某個監聽器,監聽器必須是該事件已經註冊過的監聽器。

它接受兩個引數,第一個是事件名稱,第二個是回撥函式名稱。

var callback =function(stream){
  console.log('someone connected!');};
server.on('connection', callback);// ...
server.removeListener('connection', callback);
5 removeAllListeners([event])
移除所有事件的所有監聽器, 如果指定事件,則移除指定事件的所有監聽器。
6 setMaxListeners(n)
預設情況下, EventEmitters 如果你新增的監聽器超過 10 個就會輸出警告資訊。 setMaxListeners 函式用於提高監聽器的預設限制的數量。
7 listeners(event)
返回指定事件的監聽器陣列。
8 emit(event, [arg1], [arg2], [...])
按引數的順序執行每個監聽器,如果事件有註冊監聽返回 true,否則返回 false。

類方法

序號 方法 & 描述
1 listenerCount(emitter, event)
返回指定事件的監聽器數量。

事件

序號 事件 & 描述
1 newListener
  • event - 字串,事件名稱

  • listener - 處理事件函式

該事件在新增新監聽器時被觸發。

2 removeListener
  • event - 字串,事件名稱

  • listener - 處理事件函式

從指定監聽器陣列中刪除一個監聽器。需要注意的是,此操作將會改變處於被刪監聽器之後的那些監聽器的索引。

EventEmitter 定義了一個特殊的事件 error,它包含了錯誤的語義,我們在遇到 異常的時候通常會觸發 error 事件。當 error 被觸發時,EventEmitter 規定如果沒有響 應的監聽器,Node.js 會把它當作異常,退出程式並輸出錯誤資訊。我們一般要為會觸發 error 事件的物件設定監聽器,避免遇到錯誤後整個程式崩潰。例如:
var events = require('events'); 
var emitter = new events.EventEmitter();
emitter.emit('error'); 

執行時會顯示以下錯誤:
node.js:201 
throw e; // process.nextTick error, or 'error' event on first tick 
^ 
Error: Uncaught, unspecified 'error' event. 
at EventEmitter.emit (events.js:50:15) 
at Object.<anonymous> (/home/byvoid/error.js:5:9) 
at Module._compile (module.js:441:26) 
at Object..js (module.js:459:10) 
at Module.load (module.js:348:31) 
at Function._load (module.js:308:12) 
at Array.0 (module.js:479:10) 
at EventEmitter._tickCallback (node.js:192:40) 

三、Node.js Buffer

JavaScript 語言自身只有字串資料型別,沒有二進位制資料型別。但在處理像TCP流或檔案流時,必須使用到二進位制資料。因此在 Node.js中,定義了一個 Buffer 類,該類用來建立一個專門存放二進位制資料的快取區。在 Node.js 中,Buffer 類是隨 Node 核心一起釋出的核心庫。Buffer 庫為 Node.js 帶來了一種儲存原始資料的方法,可以讓 Node.js 處理二進位制資料,每當需要在 Node.js 中處理I/O操作中移動的資料時,就有可能使用 Buffer 庫。原始資料儲存在 Buffer 類的例項中。一個 Buffer 類似於一個整數陣列,但它對應於 V8 堆記憶體之外的一塊原始記憶體。

Buffer 例項一般用於表示編碼字元的序列,比如 UTF-8 、 UCS2 、 Base64 、或十六進位制編碼的資料。 通過使用顯式的字元編碼,就可以在 Buffer 例項與普通的 JavaScript 字串之間進行相互轉換。Node.js 目前支援的字元編碼包括:

  1. ascii - 僅支援 7 位 ASCII 資料。如果設定去掉高位的話,這種編碼是非常快的。
  2. utf8 - 多位元組編碼的 Unicode 字元。許多網頁和其他文件格式都使用 UTF-8 。
  3. utf16le - 2 或 4 個位元組,小位元組序編碼的 Unicode 字元。支援代理對(U+10000 至 U+10FFFF)。
  4. ucs2 - utf16le 的別名。
  5. base64 - Base64 編碼。
  6. latin1 - 一種把 Buffer 編碼成一位元組編碼的字串的方式。
  7. binary - latin1 的別名。
  8. hex - 將每個位元組編碼為兩個十六進位制字元。

Buffer類中的方法如下:

序號 方法 & 描述
1 new Buffer(size) 
分配一個新的 size 大小單位為8位位元組的 buffer。 注意, size 必須小於 kMaxLength,否則,將會丟擲異常 RangeError。廢棄的: 使用 Buffer.alloc() 代替(或 Buffer.allocUnsafe())。
2 new Buffer(buffer) 
拷貝引數 buffer 的資料到 Buffer 例項。廢棄的: 使用 Buffer.from(buffer) 代替。
3 new Buffer(str[, encoding])
分配一個新的 buffer ,其中包含著傳入的 str 字串。 encoding 編碼方式預設為 'utf8'。 廢棄的: 使用 Buffer.from(string[, encoding]) 代替。
4 buf.length
返回這個 buffer 的 bytes 數。注意這未必是 buffer 裡面內容的大小。length 是 buffer 物件所分配的記憶體數,它不會隨著這個 buffer 物件內容的改變而改變。
5 buf.write(string[, offset[, length]][, encoding])
根據引數 offset 偏移量和指定的 encoding 編碼方式,將引數 string 資料寫入buffer。 offset 偏移量預設值是 0, encoding 編碼方式預設是 utf8。 length 長度是將要寫入的字串的 bytes 大小。 返回 number 型別,表示寫入了多少 8 位位元組流。如果 buffer 沒有足夠的空間來放整個 string,它將只會只寫入部分字串。 length 預設是 buffer.length - offset。 這個方法不會出現寫入部分字元。
6 buf.writeUIntLE(value, offset, byteLength[, noAssert])
將 value 寫入到 buffer 裡, 它由 offset 和 byteLength 決定,最高支援 48 位無符號整數,小端對齊,例如:
const buf =Buffer.allocUnsafe(6);

buf.writeUIntLE(0x1234567890ab,0,6);// 輸出: <Buffer ab 90 78 56 34 12>
console.log(buf);
noAssert 值為 true 時,不再驗證 value 和 offset 的有效性。 預設是 false。
7 buf.writeUIntBE(value, offset, byteLength[, noAssert])
將 value 寫入到 buffer 裡, 它由 offset 和 byteLength 決定,最高支援 48 位無符號整數,大端對齊。noAssert 值為 true 時,不再驗證 value 和 offset 的有效性。 預設是 false。
const buf =Buffer.allocUnsafe(6);

buf.writeUIntBE(0x1234567890ab,0,6);// 輸出: <Buffer 12 34 56 78 90 ab>
console.log(buf);
8 buf.writeIntLE(value, offset, byteLength[, noAssert])
將value 寫入到 buffer 裡, 它由offset 和 byteLength 決定,最高支援48位有符號整數,小端對齊。noAssert 值為 true 時,不再驗證 value 和 offset 的有效性。 預設是 false。
9 buf.writeIntBE(value, offset, byteLength[, noAssert])
將value 寫入到 buffer 裡, 它由offset 和 byteLength 決定,最高支援48位有符號整數,大端對齊。noAssert 值為 true 時,不再驗證 value 和 offset 的有效性。 預設是 false。
10 buf.readUIntLE(offset, byteLength[, noAssert])
支援讀取 48 位以下的無符號數字,小端對齊。noAssert 值為 true 時, offset 不再驗證是否超過 buffer 的長度,預設為 false。
11 buf.readUIntBE(offset, byteLength[, noAssert])
支援讀取 48 位以下的無符號數字,大端對齊。noAssert 值為 true 時, offset 不再驗證是否超過 buffer 的長度,預設為 false。
12 buf.readIntLE(offset, byteLength[, noAssert])
支援讀取 48 位以下的有符號數字,小端對齊。noAssert 值為 true 時, offset 不再驗證是否超過 buffer 的長度,預設為 false。
13 buf.readIntBE(offset, byteLength[, noAssert])
支援讀取 48 位以下的有符號數字,大端對齊。noAssert 值為 true 時, offset 不再驗證是否超過 buffer 的長度,預設為 false。
14 buf.toString([encoding[, start[, end]]])
根據 encoding 引數(預設是 'utf8')返回一個解碼過的 string 型別。還會根據傳入的引數 start (預設是 0) 和 end (預設是 buffer.length)作為取值範圍。
15 buf.toJSON()
將 Buffer 例項轉換為 JSON 物件。
16 buf[index]
獲取或設定指定的位元組。返回值代表一個位元組,所以返回值的合法範圍是十六進位制0x00到0xFF 或者十進位制0至 255。
17 buf.equals(otherBuffer)
比較兩個緩衝區是否相等,如果是返回 true,否則返回 false。
18 buf.compare(otherBuffer)
比較兩個 Buffer 物件,返回一個數字,表示 buf 在 otherBuffer 之前,之後或相同。
19 buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])
buffer 拷貝,源和目標可以相同。 targetStart 目標開始偏移和 sourceStart 源開始偏移預設都是 0。 sourceEnd 源結束位置偏移預設是源的長度 buffer.length 。
20 buf.slice([start[, end]])
剪下 Buffer 物件,根據 start(預設是 0 ) 和 end (預設是 buffer.length ) 偏移和裁剪了索引。 負的索引是從 buffer 尾部開始計算的。
21 buf.readUInt8(offset[, noAssert])
根據指定的偏移量,讀取一個無符號 8 位整數。若引數 noAssert 為 true 將不會驗證 offset 偏移量引數。 如果這樣 offset 可能會超出buffer 的末尾。預設是 false。
22 buf.readUInt16LE(offset[, noAssert])
根據指定的偏移量,使用特殊的 endian 位元組序格式讀取一個無符號 16 位整數。若引數 noAssert 為 true 將不會驗證 offset 偏移量引數。 這意味著 offset 可能會超出 buffer 的末尾。預設是 false。
23 buf.readUInt16BE(offset[, noAssert])
根據指定的偏移量,使用特殊的 endian 位元組序格式讀取一個無符號 16 位整數,大端對齊。若引數 noAssert 為 true 將不會驗證 offset 偏移量引數。 這意味著 offset 可能會超出 buffer 的末尾。預設是 false。
24 buf.readUInt32LE(offset[, noAssert])
根據指定的偏移量,使用指定的 endian 位元組序格式讀取一個無符號 32 位整數,小端對齊。 若引數 noAssert 為 true 將不會驗證 offset 偏移量引數。 這意味著 offset 可能會超出buffer 的末尾。預設是 false。
25 buf.readUInt32BE(offset[, noAssert])
根據指定的偏移量,使用指定的 endian 位元組序格式讀取一個無符號 32 位整數,大端對齊。 若引數 noAssert 為 true 將不會驗證 offset 偏移量引數。 這意味著 offset 可能會超出buffer 的末尾。預設是 false。
26 buf.readInt8(offset[, noAssert])
根據指定的偏移量,讀取一個有符號 8 位整數。 若引數 noAssert 為 true 將不會驗證 offset 偏移量引數。 這意味著 offset 可能會超出 buffer 的末尾。預設是 false。
27 buf.readInt16LE(offset[, noAssert])
根據指定的偏移量,使用特殊的 endian 格式讀取一個 有符號 16 位整數,小端對齊。 若引數 noAssert 為 true 將不會驗證 offset 偏移量引數。 這意味著 offset 可能會超出 buffer 的末尾。預設是 false。
28 buf.readInt16BE(offset[, noAssert])
根據指定的偏移量,使用特殊的 endian 格式讀取一個 有符號 16 位整數,大端對齊。 若引數 noAssert 為 true 將不會驗證 offset 偏移量引數。 這意味著 offset 可能會超出 buffer 的末尾。預設是 false。
29 buf.readInt32LE(offset[, noAssert])
根據指定的偏移量,使用指定的 endian 位元組序格式讀取一個有符號 32 位整數,小端對齊。 若引數 noAssert 為 true 將不會驗證 offset 偏移量引數。 這意味著 offset 可能會超出buffer 的末尾。預設是 false。
30 buf.readInt32BE(offset[, noAssert])
根據指定的偏移量,使用指定的 endian 位元組序格式讀取一個有符號 32 位整數,大端對齊。 若引數 noAssert 為 true 將不會驗證 offset 偏移量引數。 這意味著 offset 可能會超出buffer 的末尾。預設是 false。
31 buf.readFloatLE(offset[, noAssert])
根據指定的偏移量,使用指定的 endian 位元組序格式讀取一個 32 位雙浮點數,小端對齊。 若引數 noAssert 為 true 將不會驗證 offset 偏移量引數。 這意味著 offset 可能會超出buffer的末尾。預設是 false。
32 buf.readFloatBE(offset[, noAssert])
根據指定的偏移量,使用指定的 endian 位元組序格式讀取一個 32 位雙浮點數,大端對齊。 若引數 noAssert 為 true 將不會驗證 offset 偏移量引數。 這意味著 offset 可能會超出buffer的末尾。預設是 false。
33 buf.readDoubleLE(offset[, noAssert])
根據指定的偏移量,使用指定的 endian位元組序格式讀取一個 64 位雙精度數,小端對齊。 若引數 noAssert 為 true 將不會驗證 offset 偏移量引數。 這意味著 offset 可能會超出buffer 的末尾。預設是 false。
34 buf.readDoubleBE(offset[, noAssert])
根據指定的偏移量,使用指定的 endian位元組序格式讀取一個 64 位雙精度數,大端對齊。 若引數 noAssert 為 true 將不會驗證 offset 偏移量引數。 這意味著 offset 可能會超出buffer 的末尾。預設是 false。
35 buf.writeUInt8(value, offset[, noAssert])
根據傳入的 offset 偏移量將 value 寫入 buffer。注意:value 必須是一個合法的無符號 8 位整數。 若引數 noAssert 為 true 將不會驗證 offset 偏移量引數。 這意味著 value 可能過大,或者 offset 可能會超出 buffer 的末尾從而造成 value 被丟棄。 除非你對這個引數非常有把握,否則不要使用。預設是 false。
36 buf.writeUInt16LE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer。注意:value 必須是一個合法的無符號 16 位整數,小端對齊。 若引數 noAssert 為 true 將不會驗證 value 和 offset 偏移量引數。 這意味著 value 可能過大,或者 offset 可能會超出buffer的末尾從而造成 value 被丟棄。 除非你對這個引數非常有把握,否則儘量不要使用。預設是 false。
37 buf.writeUInt16BE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer。注意:value 必須是一個合法的無符號 16 位整數,大端對齊。 若引數 noAssert 為 true 將不會驗證 value 和 offset 偏移量引數。 這意味著 value 可能過大,或者 offset 可能會超出buffer的末尾從而造成 value 被丟棄。 除非你對這個引數非常有把握,否則儘量不要使用。預設是 false。
38 buf.writeUInt32LE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式(LITTLE-ENDIAN:小位元組序)將 value 寫入buffer。注意:value 必須是一個合法的無符號 32 位整數,小端對齊。 若引數 noAssert 為 true 將不會驗證 value 和 offset 偏移量引數。 這意味著value 可能過大,或者offset可能會超出buffer的末尾從而造成 value 被丟棄。 除非你對這個引數非常有把握,否則儘量不要使用。預設是 false。
39 buf.writeUInt32BE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式(Big-Endian:大位元組序)將 value 寫入buffer。注意:value 必須是一個合法的有符號 32 位整數。 若引數 noAssert 為 true 將不會驗證 value 和 offset 偏移量引數。 這意味著 value 可能過大,或者offset可能會超出buffer的末尾從而造成 value 被丟棄。 除非你對這個引數非常有把握,否則儘量不要使用。預設是 false。
40 buf.writeInt8(value, offset[, noAssert])<br根據傳入的 offset="" 偏移量將="" value="" 寫入="" buffer="" 。注意:value="" 必須是一個合法的="" signed="" 8="" 位整數。="" 若引數="" noassert="" 為="" true="" 將不會驗證="" 和="" 偏移量引數。="" 這意味著="" 可能過大,或者="" 可能會超出="" 的末尾從而造成="" 被丟棄。="" 除非你對這個引數非常有把握,否則儘量不要使用。預設是="" false。<="" td="">
41 buf.writeInt16LE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer。注意:value 必須是一個合法的 signed 16 位整數。 若引數 noAssert 為 true 將不會驗證 value 和 offset 偏移量引數。 這意味著 value 可能過大,或者 offset 可能會超出 buffer 的末尾從而造成 value 被丟棄。 除非你對這個引數非常有把握,否則儘量不要使用。預設是 false 。
42 buf.writeInt16BE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer。注意:value 必須是一個合法的 signed 16 位整數。 若引數 noAssert 為 true 將不會驗證 value 和 offset 偏移量引數。 這意味著 value 可能過大,或者 offset 可能會超出 buffer 的末尾從而造成 value 被丟棄。 除非你對這個引數非常有把握,否則儘量不要使用。預設是 false 。
43 buf.writeInt32LE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer。注意:value 必須是一個合法的 signed 32 位整數。 若引數 noAssert 為 true 將不會驗證 value 和 offset 偏移量引數。 這意味著 value 可能過大,或者 offset 可能會超出 buffer 的末尾從而造成 value 被丟棄。 除非你對這個引數非常有把握,否則儘量不要使用。預設是 false。
44 buf.writeInt32BE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer。注意:value 必須是一個合法的 signed 32 位整數。 若引數 noAssert 為 true 將不會驗證 value 和 offset 偏移量引數。 這意味著 value 可能過大,或者 offset 可能會超出 buffer 的末尾從而造成 value 被丟棄。 除非你對這個引數非常有把握,否則儘量不要使用。預設是 false。
45 buf.writeFloatLE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer 。注意:當 value 不是一個 32 位浮點數型別的值時,結果將是不確定的。 若引數 noAssert 為 true 將不會驗證 value 和 offset 偏移量引數。 這意味著 value可能過大,或者 offset 可能會超出 buffer 的末尾從而造成 value 被丟棄。 除非你對這個引數非常有把握,否則儘量不要使用。預設是 false。
46 buf.writeFloatBE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer 。注意:當 value 不是一個 32 位浮點數型別的值時,結果將是不確定的。 若引數 noAssert 為 true 將不會驗證 value 和 offset 偏移量引數。 這意味著 value可能過大,或者 offset 可能會超出 buffer 的末尾從而造成 value 被丟棄。 除非你對這個引數非常有把握,否則儘量不要使用。預設是 false。
47 buf.writeDoubleLE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer。注意:value 必須是一個有效的 64 位double 型別的值。 若引數 noAssert 為 true 將不會驗證 value 和 offset 偏移量引數。 這意味著 value 可能過大,或者 offset 可能會超出 buffer 的末尾從而造成value被丟棄。 除非你對這個引數非常有把握,否則儘量不要使用。預設是 false。
48 buf.writeDoubleBE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer。注意:value 必須是一個有效的 64 位double 型別的值。 若引數 noAssert 為 true 將不會驗證 value 和 offset 偏移量引數。 這意味著 value 可能過大,或者 offset 可能會超出 buffer 的末尾從而造成value被丟棄。 除非你對這個引數非常有把握,否則儘量不要使用。預設是 false。
49 buf.fill(value[, offset][, end])
使用指定的 value 來填充這個 buffer。如果沒有指定 offset (預設是 0) 並且 end (預設是 buffer.length) ,將會填充整個buffer。

四、Node.js Stream

Stream 是一個抽象介面,Node 中有很多物件實現了這個介面。例如,對http 伺服器發起請求的request 物件就是一個 Stream,還有stdout(標準輸出)。Node.js,Stream 有四種流型別:

  1. Readable - 可讀操作。
  2. Writable - 可寫操作。
  3. Duplex - 可讀可寫操作.
  4. Transform - 操作被寫入資料,然後讀出結果。
所有的 Stream 物件都是 EventEmitter 的例項。常用的事件有:
  1. data - 當有資料可讀時觸發。
  2. end - 沒有更多的資料可讀時觸發。
  3. error - 在接收和寫入過程中發生錯誤時觸發。
  4. finish - 所有資料已被寫入到底層系統時觸發。

如下程式碼展示了Stream ReadStream的用法:

var fs = require("fs");
var data = '';

// 建立可讀流
var readerStream = fs.createReadStream('input.txt');

// 設定編碼為 utf8。
readerStream.setEncoding('UTF8');

// 處理流事件 --> data, end, and error
readerStream.on('data', function(chunk) {
   data += chunk;
});

readerStream.on('end',function(){
   console.log(data);
});

readerStream.on('error', function(err){
   console.log(err.stack);
});

console.log("程式執行完畢");
如下程式碼顯示了Stream WriteStream的用法:
var fs = require("fs");
var data = '菜鳥教程官網地址:www.runoob.com';

// 建立一個可以寫入的流,寫入到檔案 output.txt 中
var writerStream = fs.createWriteStream('output.txt');

// 使用 utf8 編碼寫入資料
writerStream.write(data,'UTF8');

// 標記檔案末尾
writerStream.end();

// 處理流事件 --> data, end, and error
writerStream.on('finish', function() {
    console.log("寫入完成。");
});

writerStream.on('error', function(err){
   console.log(err.stack);
});

console.log("程式執行完畢");
Stream具備管道流和鏈式流兩種用法:

管道流:

var fs = require("fs");

// 建立一個可讀流
var readerStream = fs.createReadStream('input.txt');

// 建立一個可寫流
var writerStream = fs.createWriteStream('output.txt');

// 管道讀寫操作
// 讀取 input.txt 檔案內容,並將內容寫入到 output.txt 檔案中
readerStream.pipe(writerStream);

console.log("程式執行完畢");
管道流可以處理讀寫操作。

鏈式流:

鏈式是通過連線輸出流到另外一個流並建立多個流操作鏈的機制。鏈式流一般用於管道操作。

接下來我們就是用管道和鏈式來壓縮和解壓檔案。建立 compress.js 檔案, 程式碼如下:

var fs = require("fs");
var zlib = require('zlib');

// 壓縮 input.txt 檔案為 input.txt.gz
fs.createReadStream('input.txt')
  .pipe(zlib.createGzip())
  .pipe(fs.createWriteStream('input.txt.gz'));
  
console.log("檔案壓縮完成。");

五、Node.js路由

Node.js路由中對HTTP請求的URL的解析需要依賴以下urlquerystring 模組:

                   url.parse(string).query
                                           |
           url.parse(string).pathname      |
                       |                   |
                       |                   |
                     ------ -------------------
http://localhost:8888/start?foo=bar&hello=world
                                ---       -----
                                 |          |
                                 |          |
              querystring.parse(queryString)["foo"]    |
                                            |
                         querystring.parse(queryString)["hello"]
現在我們來給 onRequest() 函式加上一些邏輯,用來找出瀏覽器請求的 URL 路徑,server.js檔案的內容如下:
var http = require("http");
var url = require("url");
 
function start() {
  function onRequest(request, response) {
    var pathname = url.parse(request.url).pathname;
    console.log("Request for " + pathname + " received.");
    response.writeHead(200, {"Content-Type": "text/plain"});
    response.write("Hello World");
    response.end();
  }
 
  http.createServer(onRequest).listen(8888);
  console.log("Server has started.");
}
 
exports.start = start;
此時的server並無區分pathname對映的能力,如下新增router.js用於處理來自不同路徑的請求:
function route(pathname) {
  console.log("About to route a request for " + pathname);
}
 
exports.route = route;
首先,我們來擴充套件一下伺服器的 start() 函式,以便將路由函式作為引數傳遞過去:
var http = require("http");
var url = require("url");
 
function start(route) {
  function onRequest(request, response) {
    var pathname = url.parse(request.url).pathname;
    console.log("Request for " + pathname + " received.");
 
    route(pathname);
 
    response.writeHead(200, {"Content-Type": "text/plain"});
    response.write("Hello World");
    response.end();
  }
 
  http.createServer(onRequest).listen(8888);
  console.log("Server has started.");
}
 
exports.start = start;
同時,我們會相應擴充套件 index.js,使得路由函式可以被注入到伺服器中:
var server = require("./server");
var router = require("./router");
 
server.start(router.route);
此時請求localhost:8888時即經過了router的處理。

五、Node.js 全域性變數

JavaScript 中有一個特殊的物件,稱為全域性物件(Global Object),它及其所有屬性都可以在程式的任何地方訪問,即全域性變數。
在瀏覽器 JavaScript 中,通常 window 是全域性物件, 而 Node.js 中的全域性物件是 global,所有全域性變數(除了 global 本身以外)都是 global 物件的屬性。在 Node.js 我們可以直接訪問到 global 的屬性,而不需要在應用中包含它。

global 最根本的作用是作為全域性變數的宿主。按照 ECMAScript 的定義,滿足以下條 件的變數是全域性變數:

  1. 在最外層定義的變數;
  2. 全域性物件的屬性;
  3. 隱式定義的變數(未定義直接賦值的變數)。
當你定義一個全域性變數時,這個變數同時也會成為全域性物件的屬性,反之亦然。需要注意的是,在 Node.js 中你不可能在最外層定義變數,因為所有使用者程式碼都是屬於當前模組的, 而模組本身不是最外層上下文。
注意: 永遠使用 var 定義變數以避免引入全域性變數,因為全域性變數會汙染 名稱空間,提高程式碼的耦合風險。
Node.js中常用的全域性變數有:
  1. __filename                    __filename 表示當前正在執行的指令碼的檔名。它將輸出檔案所在位置的絕對路徑,且和命令列引數所指定的檔名不一定相同。 如果在模組中,返回的值是模組檔案的路徑。
  2. __dirname                     __dirname 表示當前執行指令碼所在的目錄。
  3. setTimeout(cb, ms)       setTimeout(cb, ms) 全域性函式在指定的毫秒(ms)數後執行指定函式(cb)。:setTimeout() 只執行一次指定函式。
  4. 返回一個代表定時器的控制代碼值。
  5. clearTimeout(t)             clearTimeout( t ) 全域性函式用於停止一個之前通過 setTimeout() 建立的定時器。 引數 t 是通過 setTimeout() 函式建立的定時器。
  6. setInterval(cb, ms)       setInterval(cb, ms) 全域性函式在指定的毫秒(ms)數後執行指定函式(cb)。返回一個代表定時器的控制代碼值。可以使用 clearInterval(t) 函式來清除定時器。setInterval() 方法會不停地呼叫函式,直到 clearInterval() 被呼叫或視窗被關閉。
  7. console                        console 用於提供控制檯標準輸出,它是由 Internet Explorer 的 JScript 引擎提供的除錯工具,後來逐漸成為瀏覽器的實施標準。Node.js 沿用了這個標準,提供與習慣行為一致的 console 物件,用於