nodejs api 中文文檔

Node.js v0.10.18 手冊 & 文檔


目錄

關於本文檔#

本文檔的目標是從參考和概念的角度全面解釋 Node.js 的 API,每章節描述一個內置模塊或高級概念。html

在某些狀況下,屬性類型、方法參數以及事件處理過程(handler)參數 會被列在主標題下的列表中。java

每個 .html 文件都對應一分內容相同的結構化 .json 文檔。這個特性如今仍是實驗性質的,但願可以爲一些須要對文檔進行操做的IDE或者其餘工具提供幫助。node

每一個 .html 和 .json 文件都是基於源碼的 doc/api/ 目錄下的 .markdown 文件生成的。本文檔使用 tools/doc/generate.js 這個程序來生產的。 HTML 模板文件爲 doc/template.htmllinux

穩定度#

在文檔中,您能夠了解每個小節的穩定性。Node.js的API會有一些小的改變,當它成熟的時候,會有些部分相比另一些來講更加可靠。有一部分接受過嚴格驗證,被大量依賴的API幾乎是不會改變的。也有一些是新增的、實驗性的或者因被證明具備危險性而在從新設計中。c++

穩定度定義以下git

穩定度: 5 - 已鎖定 除非發現嚴重缺陷,該代碼不會被更改。請不要對此區域提出更改,更改提議將被拒絕。

JSON 輸出#

穩定度: 1 - 實驗性

每一個經過 markdown 生成的 HTML 文件都對應於一個具備相同數據的 JSON 文件。程序員

該特性引入於 node v0.6.12。當前是測試性功能。github

概述#

一個輸出 「Hello World」 的簡單 Web 服務器例子:web

console.log('服務器已運行,請打開 http://127.0.0.1:8124/');

要運行這個服務器,先將程序保存爲文件 「example.js」,並使用 node 命令來執行:

> node example.js 服務器已運行,請打開 http://127.0.0.1:8124/

全部的文檔中的例子均使用相同的方式運行。

全局對象#

這些對象在全部模塊中都是可用的。有些對象實際上並不是在全局做用域內而是在模塊做用域內——這種狀況在如下文檔中會特別指出。

global#

  • {Object} 全局命名空間對象。

在瀏覽器中,頂級做用域就是全局做用域。這就是說,在瀏覽器中,若是當前是在全局做用域內,var something將會聲明一個全局變量。在Node中則不一樣。頂級做用域並不是全局做用域,在Node模塊裏的var something只屬於那個模塊。

process#

  • {Object}

進程對象。見 進程對象章節。

console#

  • {Object}

用於打印標準輸出和標準錯誤。見控制檯章節。

類: Buffer#

  • {Function}

用於處理二進制數據。見Buffer章節。

require()#

  • {Function}

引入模塊。見Modules章節。require實際上並不是全局的而是各個模塊本地的。

require.resolve()#

使用內部的require()機制查找模塊的位置,但不加載模塊,只返回解析過的模塊文件路徑。

require.cache#

  • {Object}

模塊在引入時會緩存到該對象。經過刪除該對象的鍵值,下次調用require時會從新加載相應模塊。

require.extensions#

穩定度:0 - 已廢棄
  • {Object}

指導require方法如何處理特定的文件擴展名。

.sjs文件做爲.js文件處理:

require.extensions['.sjs'] = require.extensions['.js'];

已廢棄 以前,該列表用於按需編譯非JavaScript模塊並加載進Node。然而,實踐中有更好的方式實現該功能,如經過其餘Node程序加載模塊,或提早將他們編譯成JavaScript代碼。

因爲模塊系統的API已鎖定,該功能可能永遠不會去掉。改動它可能會產生細微的錯誤和複雜性,因此最好保持不變。

__filename#

  • {String}

當前所執行代碼文件的文件路徑。這是該代碼文件通過解析後的絕對路徑。對於主程序來講,這和命令行中使用的文件路徑未必是相同的。在模塊中此變量值是該模塊文件的路徑。

例子:在/Users/mjr下運行node example.js

console.log(__filename); // /Users/mjr/example.js

__filename實際上並不是全局的而是各個模塊本地的。

__dirname#

  • {String}

當前執行腳本所在目錄的目錄名。

例子:在/Users/mjr下運行node example.js

console.log(__dirname); // /Users/mjr

__dirname實際上並不是全局的而是各個模塊本地的。

module#

  • {Object}

當前模塊的引用。特別地,module.exportsexports指向同一個對象。module實際上並不是全局的而是各個模塊本地的。

詳情可見模塊系統文檔

exports#

module.exports對象的引用,該對象被當前模塊的全部實例所共享,經過require()可訪問該對象。 什麼時候使用exports以及什麼時候使用module.exports的詳情可參見模塊系統文檔。 exports實際上並不是全局的而是各個模塊本地的。

詳情可見模塊系統文檔

關於模塊系統的更多信息可參見模塊 。

setTimeout(cb, ms)#

至少ms毫秒後調用回調cb。實際延遲取決於外部因素,如操做系統定時器粒度及系統負載。

超時值必須在1-2147483647的範圍內(包含1和2147483647)。若是該值超出範圍,則該值被看成1毫秒處理。通常來講,一個定時器不能超過24.8天。

返回一個表明該定時器的句柄值。

clearTimeout(t)#

中止一個以前經過setTimeout()建立的定時器。回調不會再被執行。

setInterval(cb, ms)#

每隔ms毫秒重複調用回調cb。注意,取決於外部因素,如操做系統定時器粒度及系統負載,實際間隔可能會改變。它不會少於ms但可能比ms長。

間隔值必須在1-2147483647的範圍內(包含1和2147483647)。若是該值超出範圍,則該值被看成1毫秒處理。通常來講,一個定時器不能超過24.8天。

返回一個表明該定時器的句柄值。

clearInterval(t)#

中止一個以前經過setInterval()建立的定時器。回調不會再被執行。

定製器函數是全局變量。見定時器章節。

控制檯#

穩定度: 4 - 凍結
  • {Object}

用於向 stdout 和 stderr 打印字符。相似於大部分 Web 瀏覽器提供的 console 對象函數,在這裏則是輸出到 stdout 或 stderr。

當輸出目標是一個終端或者文件時,console函數是同步的(爲了防止過早退出時丟失信息).當輸出目標是一個管道時它們是異步的(防止阻塞過長時間).

也就是說,在下面的例子中,stdout 是非阻塞的,而 stderr 則是阻塞的。

$ node script.js 2> error.log | tee info.log

在平常使用中,您不須要太擔憂阻塞/非阻塞的差異,除非您須要記錄大量數據。

console.log([data], [...])#

向 stdout 打印並新起一行。這個函數能夠像 printf() 那樣接受多個參數,例如:

console.log('count: %d', count);

若是在第一個字符串中沒有找到格式化元素,那麼 util.inspect 將被應用到各個參數。詳見 util.format()

console.info([data], [...])#

同 console.log

console.error([data], [...])#

同 console.log,但輸出到 stderr。

console.warn([data], [...])#

同 console.error

console.dir(obj)#

對 obj 使用 util.inspect 並將結果字符串輸出到 stdout。這個函數會忽略 obj 上的任何自定義 inspect()

console.time(label)#

標記一個時間點。

console.timeEnd(label)#

結束計時器,記錄輸出。例如:

console.time('100-elements'); for (var i = 0; i < 100; i++) { ; } console.timeEnd('100-elements');

console.trace(label)#

打印當前位置的棧跟蹤到 stderr。

console.assert(expression, [message])#

與 assert.ok() 相同,若是 expression 執行結果爲 false 則拋出一個帶上 message 的 AssertionError。

定時器#

穩定度: 5 - 已鎖定

全部的定時器函數都是全局變量. 你使用這些函數時不須要 require()模塊.

setTimeout(callback, delay, [arg], [...])#

調度 delay 毫秒後的一次 callback 執行。返回一個可能被 clearTimeout() 用到的 timeoutId。可選地,您還能給回調傳入參數。

請務必注意,您的回調有可能不會在準確的 delay 毫秒後被調用。Node.js 不保證回調被觸發的精確時間和順序。回調會在儘量接近所指定時間上被調用。

clearTimeout(timeoutId)#

阻止一個 timeout 被觸發。

setInterval(callback, delay, [arg], [...])#

調度每隔 delay 毫秒執行一次的 callback。返回一個可能被 clearInterval() 用到的 intervalId。可選地,您還能給回調傳入參數。

clearInterval(intervalId)#

中止一個 interval 的觸發。

unref()#

setTimeout 和 setInterval 所返回的值同時具備 timer.unref() 方法,容許您建立一個活動的、但當它是事件循環中僅剩的項目時不會保持程序運行的定時器。若是定時器已被 unref,再次調用 unref 不會產生其它影響。

在 setTimeout 的情景中當您 unref 您會建立另外一個定時器,並喚醒事件循環。建立太多這種定時器可能會影響事件循環的性能,慎用。

ref()#

若是您以前 unref() 了一個定時器,您能夠調用 ref() 來明確要求定時器讓程序保持運行。若是定時器已被 ref 那麼再次調用 ref 不會產生其它影響。

setImmediate(callback, [arg], [...])#

調度在全部 I/O 事件回調以後、setTimeout 和 setInterval 以前「當即」執行 callback。返回一個可能被 clearImmediate() 用到的 immediateId。可選地,您還能給回調傳入參數。

immediate 的回調以它們建立的順序被加入隊列。整個回調隊列會在每一個事件循環迭代中被處理。若是您在一個正被執行的回調中添加 immediate,那麼這個 immediate 在下一個事件循環迭代以前都不會被觸發。

clearImmediate(immediateId)#

中止一個 immediate 的觸發。

Modules#

穩定度: 5 - 已鎖定

Node有一個簡易的模塊加載系統。在node中,文件和模塊是一一對應的。下面示例是foo.js加載同一目錄下的circle.js

foo.js的內容:

var circle = require('./circle.js'); console.log( 'The area of a circle of radius 4 is ' + circle.area(4));

circle.js的內容:

var PI = Math.PI; exports.area = function (r) { return PI * r * r; }; exports.circumference = function (r) { return 2 * PI * r; };

circle.js模塊輸出了area()circumference()兩個函數。要輸出某個對象,把它加到exports這個特殊對象下便可。

注意,exportsmodule.exports的一個引用,只是爲了用起來方便。當你想輸出的是例如構造函數這樣的單個項目,那麼須要使用module.exports

// 正確輸出構造函數 module.exports = MyConstructor;

模塊內的本地變量是私有的。在這裏例子中,PI這個變量就是circle.js私有的。

模塊系統的實如今require("module")中。

循環#

當存在循環的require()調用時,一個模塊可能在返回時並不會被執行。

考慮這樣一種情形:

a.js:

console.log('a starting'); exports.done = false; var b = require('./b.js'); console.log('in a, b.done = %j', b.done); exports.done = true; console.log('a done');

b.js:

console.log('b starting'); exports.done = false; var a = require('./a.js'); console.log('in b, a.done = %j', a.done); exports.done = true; console.log('b done');

main.js:

console.log('main starting'); var a = require('./a.js'); var b = require('./b.js'); console.log('in main, a.done=%j, b.done=%j', a.done, b.done);

首先main.js加載a.js,接着a.js又去加載b.js。這時,b.js會嘗試去加載a.js。爲了防止無限的循環,a.js會返回一個unfinished copyb.js。而後b.js就會中止加載,並將其exports對象返回給a.js模塊。

這樣main.js就把這兩個模塊都加載完成了。這段程序的輸出以下:

$ node main.js main starting a starting b starting in b, a.done = false b done in a, b.done = true a done in main, a.done=true, b.done=true

若是你的程序中有循環的模塊依賴,請確保工做正常。

核心模塊#

Node中有一些模塊是編譯成二進制的。這些模塊在本文檔的其餘地方有更詳細的描述。

核心模塊定義在node源代碼的lib/目錄下。

require()老是會優先加載核心模塊。例如,require('http')老是返回編譯好的HTTP模塊,而無論是否有這個名字的文件。

文件模塊#

若是按文件名沒有查找到,那麼node會添加 .js和 .json後綴名,再嘗試加載,若是仍是沒有找到,最後會加上.node的後綴名再次嘗試加載。

.js 會被解析爲Javascript純文本文件,.json 會被解析爲JSON格式的純文本文件. .node 則會被解析爲編譯後的插件模塊,由dlopen進行加載。

模塊以'/'爲前綴,則表示絕對路徑。例如,require('/home/marco/foo.js') ,加載的是/home/marco/foo.js這個文件。

模塊以'./'爲前綴,則路徑是相對於調用require()的文件。 也就是說,circle.js必須和foo.js在同一目錄下,require('./circle')才能找到。

當沒有以'/'或者'./'來指向一個文件時,這個模塊要麼是"核心模塊",要麼就是從node_modules文件夾加載的。

若是指定的路徑不存在,require()會拋出一個code屬性爲'MODULE_NOT_FOUND'的錯誤。

node_modules文件夾中加載#

若是require()中的模塊名不是一個本地模塊,也沒有以'/''../', 或是 './'開頭,那麼node會從當前模塊的父目錄開始,嘗試在它的/node_modules文件夾里加載相應模塊。

若是沒有找到,那麼就再向上移動到父目錄,直到到達頂層目錄位置。

例如,若是位於'/home/ry/projects/foo.js'的文件調用了require('bar.js'),那麼node查找的位置依次爲:

  • /home/ry/projects/node_modules/bar.js
  • /home/ry/node_modules/bar.js
  • /home/node_modules/bar.js
  • /node_modules/bar.js

這就要求程序員應儘可能把依賴放在就近的位置,以防崩潰。

Folders as Modules#

能夠把程序和庫放到一個單獨的文件夾裏,並提供單一入口來指向它。有三種方法,使一個文件夾能夠做爲require()的參數來加載。

首先是在文件夾的根目錄建立一個叫作package.json的文件,它須要指定一個main模塊。下面是一個package.json文件的示例。

{ "name" : "some-library", "main" : "./lib/some-library.js" }

示例中這個文件,若是是放在./some-library目錄下面,那麼require('./some-library')就將會去加載./some-library/lib/some-library.js

This is the extent of Node's awareness of package.json files.

若是目錄裏沒有package.json這個文件,那麼node就會嘗試去加載這個路徑下的index.js或者index.node。例如,若上面例子中,沒有package.json,那麼require('./some-library')就將嘗試加載下面的文件:

  • ./some-library/index.js
  • ./some-library/index.node

Caching#

模塊在第一次加載後會被緩存。這意味着(相似其餘緩存)每次調用require('foo')的時候都會返回同一個對象,固然,必須是每次都解析到同一個文件。

Multiple calls to require('foo') may not cause the module code to be executed multiple times. This is an important feature. With it, "partially done" objects can be returned, thus allowing transitive dependencies to be loaded even when they would cause cycles.

若是你但願一個模塊屢次執行,那麼就輸出一個函數,而後調用這個函數。

Module Caching Caveats#

模塊的緩存是依賴於解析後的文件名。因爲隨着調用的位置不一樣,可能解析到不一樣的文件(好比需從node_modules文件夾加載的狀況),因此,若是解析到其餘文件時,就不能保證require('foo')老是會返回確切的同一對象。

The module Object#

  • {Object}

在每個模塊中,變量 module 是一個表明當前模塊的對象的引用。 特別地,module.exports 能夠經過全局模塊對象 exports 獲取到。 module 不是事實上的全局對象,而更像是每一個模塊內部的。

module.exports#

  • {Object}

module.exports 對象是經過模塊系統產生的。有時候這是難以接受的,許多人想讓他們的模塊是某個類的實例。 所以,將要導出的對象賦值給 module.exports 。例如,假設咱們有一個模塊稱之爲 a.js

// Do some work, and after some time emit // the 'ready' event from the module itself. setTimeout(function() { module.exports.emit('ready'); }, 1000);

那麼,在另外一個文件中咱們能夠這樣寫

var a = require('./a'); a.on('ready', function() { console.log('module a is ready'); });

Note that assignment to module.exports must be done immediately. It cannot be done in any callbacks. This does not work:

x.js:

setTimeout(function() { module.exports = { a: "hello" }; }, 0);

y.js:

var x = require('./x'); console.log(x.a);

module.require(id)#

  • id {String}
  • Return: {Object} 已解析模塊的 module.exports

module.require 方法提供了一種像 require() 同樣從最初的模塊加載一個模塊的方法。

注意,爲了這樣作,你必須取得一個對 module 對象的引用。 require() 返回 module.exports,而且 module 是一個典型的只能在特定模塊做用域內有效的變量,若是要使用它,就必須明確的導出。

module.id#

  • {String}

用於區別模塊的標識符。一般是徹底解析後的文件名。

module.filename#

  • {String}

模塊徹底解析後的文件名。

module.loaded#

  • {Boolean}

不論該模塊是否加載完畢,或者正在加載的過程當中。

module.parent#

  • {Module Object}

引入這個模塊的模塊。

module.children#

  • {Array}

這個模塊引入的全部模塊對象。

整體來講...#

爲了獲取調用 require 加載的確切的文件名,使用 require.resolve() 函數。

綜上所述,下面用僞代碼的高級算法形式表達了 require.resolve 是如何工做的:

NODE_MODULES_PATHS(START) 1. let PARTS = path split(START) 2. let ROOT = index of first instance of "node_modules" in PARTS, or 0 3. let I = count of PARTS - 1 4. let DIRS = [] 5. while I > ROOT, a. if PARTS[I] = "node_modules" CONTINUE c. DIR = path join(PARTS[0 .. I] + "node_modules") b. DIRS = DIRS + DIR c. let I = I - 1 6. return DIRS

從全局文件夾加載#

若是 NODE_PATH 環境變量設置爲一個以冒號分割的絕對路徑的列表, 找不到模塊時 node 將會從這些路徑中搜索模塊。 (注意:在 windows 操做系統上,NODE_PATH 是以分號間隔的)

此外,node 將會搜索如下地址:

  • 1: $HOME/.node_modules
  • 2: $HOME/.node_libraries
  • 3: $PREFIX/lib/node

$HOME 是用戶的主目錄,$PREFIX 是 node 裏配置的 node_prefix 。

這些大可能是因爲歷史緣由。強烈建議讀者將所依賴的模塊放到 node_modules 文件夾裏。 這樣加載的更快也更可靠。

訪問主模塊#

當 Node 直接運行一個文件時,require.main 就被設置爲它的 module 。 也就是說你能夠判斷一個文件是不是直接被運行的

require.main === module

對於一個 foo.js 文件,若是經過 node foo.js 運行是 true ,可是經過 require('./foo') 運行倒是 false 。

由於 module 提供了一個 filename 屬性(一般等於 __filename), 因此當前程序的入口點能夠經過 require.main.filename 來獲取。

附錄: 包管理技巧#

Node 的 require() 函數的語義被設計的足夠通用化,以支持各類常規目錄結構。 包管理程序如 dpkg,rpm 和 npm 將不用修改就可以從 Node 模塊構建本地包。

接下來咱們將給你一個可行的目錄結構建議:

假設咱們但願將一個包的指定版本放在 /usr/lib/node/<some-package>/<some-version> 目錄中。

包能夠依賴於其餘包。爲了安裝包 foo,可能須要安裝包 bar 的一個指定版本。 包 bar 也可能有依賴關係,在某些狀況下依賴關係可能發生衝突或者造成循環。

由於 Node 會查找它所加載的模塊的真實路徑(也就是說會解析符號連接), 而後按照上文描述的方式在 node_modules 目錄中尋找依賴關係,這種情形跟如下體系結構很是相像:

  • /usr/lib/node/foo/1.2.3/ - foo 包 1.2.3 版本的內容
  • /usr/lib/node/bar/4.3.2/ - foo 包所依賴的 bar 包的內容
  • /usr/lib/node/foo/1.2.3/node_modules/bar - 指向 /usr/lib/node/bar/4.3.2/ 的符號連接
  • /usr/lib/node/bar/4.3.2/node_modules/* - 指向 bar 包所依賴的包的符號連接

所以即使存在循環依賴或依賴衝突,每一個模塊仍是能夠得到他所依賴的包的一個可用版本。

當 foo 包中的代碼調用 require('bar'),將得到符號連接 /usr/lib/node/foo/1.2.3/node_modules/bar 指向的版本。 而後,當 bar 包中的代碼調用 require('queue'),將會得到符號連接 /usr/lib/node/bar/4.3.2/node_modules/quux 指向的版本。

此外,爲了進一步優化模塊搜索過程,不要將包直接放在 /usr/lib/node 目錄中,而是將它們放在 /usr/lib/node_modules/<name>/<version> 目錄中。 這樣在依賴的包找不到的狀況下,就不會一直尋找 /usr/node_modules 目錄或 /node_modules 目錄了。

爲了使模塊在 node 的 REPL 中可用,你可能須要將 /usr/lib/node_modules 目錄加入到 $NODE_PATH 環境變量中。 因爲在 node_modules 目錄中搜索模塊使用的是相對路徑,基於調用 require() 的文件所在真實路徑,所以包自己能夠放在任何位置。

Addons插件#

Addons插件就是動態鏈接庫。它相似膠水,將c、c++和Node粘貼起來。它的API(目前來講)至關複雜,涉及到了幾個類庫的知識。

  • V8 JavaScript引擎,一個 C++ 類庫. 用於和JavaScript進行交互的接口。 建立對象, 調用函數等. 文檔大部分在這裏: v8.h 頭文件 (deps/v8/include/v8.h在Node源代碼目錄裏), 也有可用的線上文檔 線上. (譯者:想要學習c++的addons插件編寫,必須先了解v8的接口)
  • libuv, C語言編寫的事件循環類庫。任什麼時候候須要等待一個文件描述符變爲可讀狀態,等待一個定時器,或者等待一個接受信號都須要使用libuv類庫的接口。也就是說,若是你執行任何I/O操做,libuv類庫將會被用到。
  • 內部 Node 類庫.最重要的接口就是 node::ObjectWrap 類,這個類你應該是最可能想要派生的。
  • 其餘.請參閱 deps/ 得到更多可用類庫。

Node 靜態編譯了全部依賴到它的可執行文件中去了。當編譯你的模塊時,你沒必要擔憂沒法鏈接上述那些類庫。 (譯者:換而言之,你在編譯本身的addons插件時,只管在頭部 #include <uv.h>,沒必要在binding.gyp中聲明)

下面全部的例子均可如下載到: 下載 這或許能成爲你學習和創做本身addon插件的起點。

Hello world(世界你好)#

做爲開始,讓咱們用編寫一個小的addon插件,這個addon插件的c++代碼至關於下面的JavaScript代碼。

module.exports.hello = function() { return 'world'; };

首先咱們建立一個 hello.cc文件:

NODE_MODULE(hello, init)//譯者:將addon插件名hello和上述init函數關聯輸出

注意全部Node的addons插件都必須輸出一個初始化函數:

void Initialize (Handle<Object> exports); NODE_MODULE(module_name, Initialize)

NODE_MODULE以後沒有分號,由於它不是一個函數(請參閱node.h

這個module_name(模塊名)須要和最後編譯生成的2進制文件名(減去.node後綴名)相同。

源代碼須要生成在hello.node,這個2進制addon插件中。 須要作到這些,咱們要建立一個名爲binding.gyp的文件,它描述了建立這個模塊的配置,而且它的格式是相似JSON的。 文件將被命令:node-gyp 編譯。

{ "targets": [ { "target_name": "hello", //譯者:addon插件名,注意這裏的名字必需和上面NODE_MODULE中的一致 "sources": [ "hello.cc" ] //譯者:這是須要編譯的源文件 } ] }

下一步是根據當前的操做系統平臺,利用node-gyp configure命令,生成合適的項目文件。

如今你會有一個Makefile (在Unix平臺) 或者一個 vcxproj file (在Windows上),它們都在build/ 文件夾中. 而後執行命令 node-gyp build進行編譯。 (譯者:固然你能夠執行 node-gyp rebuild一步搞定)

如今你已經有了編譯好的 .node 文件了,這個編譯好的綁定文件會在目錄 build/Release/

如今你能夠使用這個2進制addon插件在Node項目hello.js 中了,經過指明require這個剛剛建立的hello.node模塊使用它。

console.log(addon.hello()); // 'world'

請閱讀下面的內容得到更多詳情或者訪問https://github.com/arturadib/node-qt獲取一個生產環境的例子。

Addon patterns(插件方式)#

下面是一些幫助你開始編寫addon插件的方式。參考這個在線的v8 手冊用來幫助你調用各類v8接口, 而後是v8的 嵌入式開發嚮導 ,解釋幾個概念,如 handles, scopes,function templates等。

爲了能跑起來這些例子,你必須用 node-gyp 來編譯他們。 建立一個binding.gyp 文件:

{ "targets": [ { "target_name": "addon", "sources": [ "addon.cc" ] } ] }

事實上能夠有多個  .cc 文件, 就簡單的在 sources 數組裏加上便可,例子:

"sources": ["addon.cc", "myexample.cc"]

如今你有了你的binding.gyp文件了,你可要開始執行configure 和 build 命令構建你的addon插件了

$ node-gyp configure build

Function arguments(函數參數)#

下面的部分說明了如何從JavaScript的函數調用得到參數而後返回一個值。這是主要的內容而且僅須要源代碼addon.cc

NODE_MODULE(addon, Init)

你能夠使用下面的JavaScript代碼片斷來測試它

console.log( 'This should be eight:', addon.add(3,5) );

Callbacks(回調)#

你能夠傳遞JavaScript functions 到一個C++ function 而且執行他們,這裏是 addon.cc文件:

NODE_MODULE(addon, Init)

注意這個例子對Init()使用了兩個參數,將完整的 module 對象做爲第二個參數傳入。這容許addon插件徹底的重寫 exports,這樣就能夠用一個函數代替多個函數做爲exports的屬性了。

你能夠使用下面的JavaScript代碼片斷來測試它

addon(function(msg){ console.log(msg); // 'hello world' });

Object factory(對象工廠)#

在這個addon.cc文件裏用一個c++函數,你能夠建立而且返回一個新的對象,這個新的對象擁有一個msg的屬性,它的值是經過createObject()方法傳入的

NODE_MODULE(addon, Init)

在js中測試以下:

var obj1 = addon('hello'); var obj2 = addon('world'); console.log(obj1.msg+' '+obj2.msg); // 'hello world'

Function factory(函數工廠)#

此次將展現如何建立並返回一個JavaScript function函數,這個函數實際上是經過c++包裝的。

NODE_MODULE(addon, Init)

測試它:

var fn = addon(); console.log(fn()); // 'hello world'

Wrapping C++ objects(包裝c++對象)#

這裏將建立一個被c++包裹的對象或類MyObject,它是能夠在JavaScript中經過new操做符實例化的。 首先咱們要準備主要的模塊文件addon.cc:

NODE_MODULE(addon, InitAll)

而後在myobject.h文件中建立你的包裝類,它繼承自 node::ObjectWrap:

#endif

在文件 myobject.cc 能夠實施各類你想要暴露給js的方法。 這裏咱們暴露方法名爲 plusOne給就是,它表示將構造函數的屬性加1.

return scope.Close(Number::New(obj->counter_)); }

測試它:

var obj = new addon.MyObject(10); console.log( obj.plusOne() ); // 11 console.log( obj.plusOne() ); // 12 console.log( obj.plusOne() ); // 13

Factory of wrapped objects(工廠包裝對象)#

這是很是有用的,當你想建立原生的JavaScript對象時,又不想明確的使用JavaScript的new操做符。

var obj = addon.createObject(); // 用上面的方式代替下面的: // var obj = new addon.Object();

讓咱們註冊在 addon.cc 文件中註冊createObject方法:

NODE_MODULE(addon, InitAll)

myobject.h文件中,咱們如今介紹靜態方法NewInstance,它可以實例化對象(舉個例子,它的工做就像是 在JavaScript中的new` 操做符。)

#endif

這裏的處理方式和上面的 myobject.cc很像:

return scope.Close(Number::New(obj->counter_)); }

測試它:

var obj2 = createObject(20); console.log( obj2.plusOne() ); // 21 console.log( obj2.plusOne() ); // 22 console.log( obj2.plusOne() ); // 23

Passing wrapped objects around(傳遞包裝的對象)#

除了包裝和返回c++對象之外,你能夠傳遞他們而且經過Node的node::ObjectWrap::Unwrap幫助函數解包裝他們。 在下面的addon.cc 文件中,咱們介紹了一個函數add(),它可以獲取2個MyObject對象。

NODE_MODULE(addon, InitAll)

爲了使事情變得有趣,咱們在 myobject.h 採用一個公共的方法,因此咱們可以在unwrapping解包裝對象以後使用私有成員的值。

#endif

myobject.cc文件的處理方式和前面相似

return scope.Close(instance); }

測試它:

var obj1 = addon.createObject(10); var obj2 = addon.createObject(20); var result = addon.add(obj1, obj2);
console.log(result); // 30

process#

process對象是一個全局對象,能夠在任何地方訪問到它。 它是EventEmitter的一個實例。

Exit Codes#

Node 執行程序正常狀況下會返回 0,這也意味着,包括全部「異步」在內的操做都已結束。(筆者注:linux terminal 下使用 echo $? 查看,win cmd 下使用 echo %ERRORLEVEL% 查看)除此以外的其餘返回狀態以下:

  • 1 未捕獲的致命異常(Uncaught Fatal Exception) - There was an uncaught exception, and it was not handled by a domain or an uncaughtException event handler.
  • 2 - 未使用(Unused) (reserved by Bash for builtin misuse)
  • 3 解析錯誤(Internal JavaScript Parse Error) - The JavaScript source code internal in Node's bootstrapping process caused a parse error. This is extremely rare, and generally can only happen during development of Node itself.
  • 4 評估失敗(Internal JavaScript Evaluation Failure) - The JavaScript source code internal in Node's bootstrapping process failed to return a function value when evaluated. This is extremely rare, and generally can only happen during development of Node itself.
  • 5 致命錯誤(Fatal Error) - There was a fatal unrecoverable error in V8. Typically a message will be printed to stderr with the prefix FATAL ERROR.
  • 6 未正確的異常處理(Non-function Internal Exception Handler) - There was an uncaught exception, but the internal fatal exception handler function was somehow set to a non-function, and could not be called.
  • 7 異常處理函數運行時失敗(Internal Exception Handler Run-Time Failure) - There was an uncaught exception, and the internal fatal exception handler function itself threw an error while attempting to handle it. This can happen, for example, if a process.on('uncaughtException') or domain.on('error')handler throws an error.
  • 8 - 未使用(Unused). In previous versions of Node, exit code 8 sometimes indicated an uncaught exception.
  • 9 - 無效的參數(Invalid Argument) - Either an unknown option was specified, or an option requiring a value was provided without a value.
  • 10 運行時失敗(Internal JavaScript Run-Time Failure) - The JavaScript source code internal in Node's bootstrapping process threw an error when the bootstrapping function was called. This is extremely rare, and generally can only happen during development of Node itself.
  • 12 無效的調試參數(Invalid Debug Argument) - The --debug and/or --debug-brk options were set, but an invalid port number was chosen.
  • >128 信號退出(Signal Exits) - If Node receives a fatal signal such as SIGKILL or SIGHUP, then its exit code will be 128 plus the value of the signal code. This is a standard Unix practice, since exit codes are defined to be 7-bit integers, and signal exits set the high-order bit, and then contain the value of the signal code.

事件: 'exit'#

當進程將要退出時觸發。這是一個在固定時間檢查模塊狀態(如單元測試)的好時機。須要注意的是 'exit' 的回調結束後,主事件循環將再也不運行,因此計時器也會失效。

監聽 exit 事件的例子:

process.on('exit', function() { // 設置一個延遲執行 setTimeout(function() { console.log('主事件循環已中止,因此不會執行'); }, 0); console.log('退出前執行'); });

事件: 'uncaughtException'(未捕獲錯誤)#

當一個異常冒泡迴歸到事件循環中就會觸發這個事件,若是創建了一個監聽器來監聽這個異常,默認的行爲(打印堆棧跟蹤信息並退出)就不會發生。

監聽 uncaughtException 示例:

// 故意製造一個異常,並且不catch捕獲它. nonexistentFunc(); console.log('This will not run.');

注意,uncaughtException未捕獲異常是一個很是粗略的異常處理。

儘可能不要使用它,使用 domains 來代替它,若是你已經使用了,請在不處理這個異常以後重啓你的應用。

請 不要 象使用node.js的有錯誤回覆執行這樣使用.一個未處理異常意味着你的應用和你的擴展Node.js自身是有未知狀態的。盲目的恢復意味着任何事情均可能發生。

你在升級的系統時拉掉了電源線,而後恢復了。可能10次裏有9次每一偶問題,可是第10次,你的系統就會崩潰。

你已經被警告。

Signal Events#

當進程接收到信號時觸發。信號列表詳見 POSIX 標準的 sigaction(2)如 SIGINT、SIGUSR1 等。

監聽 SIGINT 信號的示例:

// 設置 'SIGINT' 信號觸發事件 process.on('SIGINT', function() { console.log('收到 SIGINT 信號。 退出請使用 Ctrl + D '); });

在大多數終端下,一個發送 SIGINT 信號的簡單方法是按下 ctrl + c 。

process.stdout#

一個指向標準輸出流(stdout)的 可寫的流(Writable Stream)

舉例: console.log 的實現

console.log = function(d) { process.stdout.write(d + '\n'); }; 

process.stderr 和 process.stdout 不像 Node 中其餘的流(Streams) 那樣,他們一般是阻塞式的寫入。當其引用指向 普通文件 或者 TTY文件描述符 時他們就是阻塞的(注:TTY 能夠理解爲終端的一種,可聯想 PuTTY,詳見百科)。當他們引用指向管道(pipes)時,他們就同其餘的流(Streams)同樣是非阻塞的。

要檢查 Node 是否正在運行一個 TTY上下文 中(注:linux 中沒有運行在 tty 下的進程是 守護進程 ),能夠用使用 process.stderr、process.stdout 或 process.stdin 的 isTTY 屬性:

$ node -p "Boolean(process.stdout.isTTY)" true $ node -p "Boolean(process.stdout.isTTY)" | cat false 

更多信息,請查看 tty 文檔

process.stderr#

一個指向標準錯誤流(stderr)的 可寫的流(Writable Stream)。

process.stderr 和 process.stdout 不像 Node 中其餘的流(Streams) 那樣,他們一般是阻塞式的寫入。當其引用指向 普通文件 或者 TTY文件描述符 時他們就是阻塞的(注:TTY 能夠理解爲終端的一種,可聯想 PuTTY,詳見百科)。當他們引用指向管道(pipes)時,他們就同其餘的流(Streams)同樣是非阻塞的。

process.stdin#

一個指向 標準輸入流(stdin) 的可讀流(Readable Stream)。標準輸入流默認是暫停 (pause) 的,因此必需要調用 process.stdin.resume() 來恢復 (resume) 接收。

打開標準輸入流,並監聽兩個事件的示例:

process.stdin.on('end', function() { process.stdout.write('end'); }); // gets 函數的簡單實現 function gets(cb){ process.stdin.resume(); process.stdin.setEncoding('utf8'); process.stdin.on('data', function(chunk) { process.stdin.pause(); cb(chunk); }); } gets(function(reuslt){ console.log("["+reuslt+"]"); });

process.argv#

一個包含命令行參數的數組。第一個元素會是 'node', 第二個元素將是 .Js 文件的名稱。接下來的元素依次是命令行傳入的參數。

// 打印 process.argv process.argv.forEach(function(val, index, array) { console.log(index + ': ' + val); });

輸出將會是:

$ node process-2.js one two=three four 0: node 1: /Users/mjr/work/node/process-2.js 2: one 3: two=three 4: four 

process.execPath#

開啓當前進程的這個可執行文件的絕對路徑。

示例:

/usr/local/bin/node 

process.execArgv#

與 process.argv 相似,不過是用於保存 node特殊(node-specific) 的命令行選項(參數)。這些特殊的選項不會出如今 process.argv 中,並且 process.execArgv 不會保存 process.argv 中保存的參數(如 0:node 1:文件名 2.3.4.參數 等), 全部文件名以後的參數都會被忽視。這些選項能夠用於派生與與父進程相同執行環境的子進程。

示例:

$ node --harmony script.js --version 

process.execArgv 中的特殊選項:

['--harmony'] 

process.argv 接收到的參數:

['/usr/local/bin/node', 'script.js', '--version'] 

process.abort()#

這將致使 Node 觸發一個abort事件,這會致使Node退出而且建立一個核心文件。

process.chdir(directory)#

改變進程的當前進程的工做目錄,若操做失敗則拋出異常。 

console.log('當前目錄:' + process.cwd()); try { process.chdir('/tmp'); console.log('新目錄:' + process.cwd()); } catch (err) { console.log('chdir: ' + err); }

process.cwd()#

返回進程當前的工做目錄。 

console.log('當前目錄:' + process.cwd());

process.env#

一個包括用戶環境的對象。詳細參見 environ(7)。

process.exit([code])#

終止當前進程並返回給定的 code。若是省略了 code,退出是會默認返回成功的狀態碼('success' code) 也就是 0

退出並返回失敗的狀態 ('failure' code):

process.exit(1); 

執行上述代碼,用來執行 node 的 shell 就能收到值爲 1 的 exit code

process.exitCode#

當進程既正常退出,或者經過未指定 code 的 process.exit() 退出時,這個屬性中所存儲的數字將會成爲進程退出的錯誤碼 (exit code)。

若是指名了 process.exit(code) 中退出的錯誤碼 (code),則會覆蓋掉 process.exitCode 的設置。

process.getgid()#

注意: 該函數僅適用於遵循 POSIX 標準的系統平臺如 Unix、Linux等 而 Windows、 Android 等則不適用。

獲取進程的羣組標識(詳見getgid(2))。獲取到的是羣組的數字ID,不是羣組名稱。

if (process.getgid) { console.log('當前 gid: ' + process.getgid()); }

process.setgid(id)#

注意: 該函數僅適用於遵循 POSIX 標準的系統平臺如 Unix、Linux等 而 Windows、 Android 等則不適用。

設置進程的羣組標識(詳見getgid(2))。參數能夠是一個數字ID或者羣組名字符串。若是指定了一個羣組名,這個方法會阻塞等待將羣組名解析爲數字ID。 

if (process.getgid && process.setgid) { console.log('當前 gid: ' + process.getgid()); try { process.setgid(501); console.log('新 gid: ' + process.getgid()); } catch (err) { console.log('設置 gid 失敗: ' + err); } }

process.getuid()#

注意: 該函數僅適用於遵循 POSIX 標準的系統平臺如 Unix、Linux等 而 Windows、 Android 等則不適用。

獲取執行進程的用戶ID(詳見getgid(2))。這是用戶的數字ID,不是用戶名。

if (process.getuid) { console.log('當前 uid: ' + process.getuid()); }

process.setuid(id)#

注意: 該函數僅適用於遵循 POSIX 標準的系統平臺如 Unix、Linux等 而 Windows、 Android 等則不適用。

設置執行進程的用戶ID(詳見getgid(2))。參數能夠使一個數字ID或者用戶名字符串。若是指定了一個用戶名,那麼該方法會阻塞等待將用戶名解析爲數字ID。

if (process.getuid && process.setuid) { console.log('當前 uid: ' + process.getuid()); try { process.setuid(501); console.log('新 uid: ' + process.getuid()); } catch (err) { console.log('設置 uid 失敗: ' + err); } }

process.getgroups()#

注意: 該函數僅適用於遵循 POSIX 標準的系統平臺如 Unix、Linux等 而 Windows、 Android 等則不適用。

返回一個保存補充組ID(supplementary group ID)的數組。POSIX 標準沒有指名 若是有效組 ID(effective group ID)被包括在內的狀況,而在 node.js 中則確保它始終是。(POSIX leaves it unspecified if the effective group ID is included but node.js ensures it always is. )

process.setgroups(groups)#

注意: 該函數僅適用於遵循 POSIX 標準的系統平臺如 Unix、Linux等 而 Windows、 Android 等則不適用。

設置補充分組的ID標識. 這是一個特殊的操做, 意味着你必須擁有root或者CAP_SETGID權限才能夠。(譯者:CAP_SETGID表示設定程序容許普通用戶使用setgid函數,這與文件的setgid權限位無關)

這個列表能夠包括分組的ID表示,或分組名或二者都有。

process.initgroups(user, extra_group)#

注意: 該函數僅適用於遵循 POSIX 標準的系統平臺如 Unix、Linux等 而 Windows、 Android 等則不適用。

讀取 /etc/group 而且初始化group分組訪問列表,使用改爲員所在的全部分組, 這是一個特殊的操做, 意味着你必須擁有root或者CAP_SETGID權限才能夠。

user 是一個用戶名或者用戶ID. extra_group是分組的組名或者分組ID。

有時候,當你在註銷權限 (dropping privileges) 的時候須要注意。例如: 

console.log(process.getgroups()); // [ 0 ] process.initgroups('bnoordhuis', 1000); // switch user console.log(process.getgroups()); // [ 27, 30, 46, 1000, 0 ] process.setgid(1000); // drop root gid console.log(process.getgroups()); // [ 27, 30, 46, 1000 ]

process.version#

一個暴露編譯時存儲版本信息的內置變量 NODE_VERSION 的屬性。

console.log('版本: ' + process.version);

process.versions#

一個暴露存儲 node 以及其依賴包 版本信息的屬性。

console.log(process.versions); 

輸出:

{ http_parser: '1.0', node: '0.10.4', v8: '3.14.5.8', ares: '1.9.0-DEV', uv: '0.10.3', zlib: '1.2.3', modules: '11', openssl: '1.0.1e' }

process.config#

一個包含用來編譯當前 node.exe 的配置選項的對象。內容與運行 ./configure 腳本生成的 "config.gypi" 文件相同。

最可能的輸出示例以下:

{ target_defaults: { cflags: [], default_configuration: 'Release', defines: [], include_dirs: [], libraries: [] }, variables: { host_arch: 'x64', node_install_npm: 'true', node_prefix: '', node_shared_cares: 'false', node_shared_http_parser: 'false', node_shared_libuv: 'false', node_shared_v8: 'false', node_shared_zlib: 'false', node_use_dtrace: 'false', node_use_openssl: 'true', node_shared_openssl: 'false', strict_aliasing: 'true', target_arch: 'x64', v8_use_snapshot: 'true' } }

process.kill(pid, [signal])#

向進程發送一個信號。 pid 是進程的 id 而 signal 則是描述信號的字符串名稱。信號的名稱都形似 'SIGINT' 或者 'SIGUSR1'。若是沒有指定參數則會默認發送 'SIGTERM' 信號,更多信息請查看 kill(2) 。

值得注意的是,這個函數的名稱雖然是 process.kill, 但就像 kill 系統調用(詳見《Unix高級編程》)同樣,它僅僅只是一個信號發送器。而信號的發送不只僅只是用來殺死(kill)目標進程。

向當前進程發送信號的示例:

process.kill(process.pid, 'SIGHUP'); 

process.pid#

當前進程的 PID 

console.log('當前進程 id: ' + process.pid);

process.title#

獲取/設置 (Getter/setter) 'ps' 中顯示的進程名。

當設置該屬性時,所能設置的字符串最大長度視具體平臺而定,若是超過的話會自動截斷。

在 Linux 和 OS X 上,它受限於名稱的字節長度加上命令行參數的長度,由於它有覆蓋參數內存(argv memory)。

v0.8 版本容許更長的進程標題字符串,也支持覆蓋環境內存,可是存在潛在的不安全和混亂(很難說清楚)。

process.arch#

返回當前 CPU 處理器的架構:'arm'、'ia32' 或者 'x64'.

console.log('當前CPU架構是:' + process.arch);

process.platform#

返回當前程序運行的平臺:'darwin''freebsd''linux''sunos' 或者 'win32'

console.log('當前系統平臺是: ' + process.platform);

process.memoryUsage()#

返回一個對象,它描述了Node進程的內存使用狀況單位是bytes。 

console.log(util.inspect(process.memoryUsage())); 

輸出將會是:

{ rss: 4935680, heapTotal: 1826816, heapUsed: 650472 } 

heapTotal 和 heapUsed 是根據 V8引擎的內存使用狀況來的

process.nextTick(callback)#

  • callback {Function}

在事件循環的下一次循環中調用 callback 回調函數。

這 不是 setTimeout(fn, 0) 函數的一個簡單別名,由於它的效率高多了。該函數能在任何 I/O 事前以前調用咱們的回調函數。可是這個函數在層次超過某個限制的時候,也會出現瑕疵,詳細見 process.maxTickDepth

console.log('開始'); process.nextTick(function() { console.log('nextTick 回調'); }); console.log('已設定'); // 輸出: // 開始 // 已設定 // nextTick 回調

若是你想要在【對象建立】以後而【I/O 操做】發生以前執行某些操做,那麼這個函數對你而言就十分重要了。

// thing.startDoingStuff() 如今被調用了, 而不是以前.

【注意!!】保證你的函數必定是同步執行或者必定是異步執行,這很是重要!!參考以下的例子:

fs.stat('file', cb); } 

這樣執行是很危險。若是你還不清楚上述行爲的危害請看下面的例子:

maybeSync(true, function() { foo(); }); bar(); 

那麼,使用剛纔那個不知道是同步仍是異步的操做,在編程的時候你就會發現,你不能肯定究竟是 foo() 先執行,仍是 bar() 先執行。

用下面的方法就能夠更好的解決:

fs.stat('file', cb); } 

注意:nextTick 的隊列會在徹底執行完畢以後才調用 I/O 操做 (the nextTick queue is completely drained on each pass of the event loop before additional I/O is processed.) 。所以,遞歸設置 nextTick 的回調就像一個 while(true) ; 循環同樣,將會阻止任何 I/O 操做的發生。

process.umask([mask])#

設置或者讀取進程的文件模式的建立掩碼。子進程從父進程中繼承這個掩碼。若是設定了參數 mask 那麼返回舊的掩碼,不然返回當前的掩碼。

oldmask = process.umask(newmask); console.log('原掩碼: ' + oldmask.toString(8) + '\n' '新掩碼: ' + newmask.toString(8));

process.uptime()#

返回 Node 程序已運行的秒數。

process.hrtime()#

返回當前的高分辨時間,形式爲 [秒,納秒] 的元組數組。它是相對於在過去的任意時間。該值與日期無關,所以不受時鐘漂移的影響。主要用途是能夠經過精確的時間間隔,來衡量程序的性能。

你能夠將前一個 process.hrtime() 的結果傳遞給當前的 process.hrtime() 函數,結果會返回一個比較值,用於基準和衡量時間間隔。

console.log('基準相差 %d 納秒', diff[0] * 1e9 + diff[1]); // 基準相差 1000000527 納秒 }, 1000);

utils#

穩定度: 4 - 凍結

若是你想使用模塊 'util'中已定義的方法. 只需 require('util') 便可使用.

util模塊設計的主要目的是爲了知足Node內部API的需求 。這個模塊中的不少方法在你編寫Node程序的時候都是頗有幫助的。若是你以爲提供的這些方法知足不了你的需求,那麼咱們鼓勵你編寫本身的實用工具方法。咱們 不但願util模塊中添加任何對於Node的內部功能非必要的擴展。

util.debuglog(section)#

  • section {String} 被調試的程序節點部分
  • 返回值: {Function} 日誌處理函數

這個方法是在存在NODE_DEBUG環境變量的基礎上,建立一個有條件寫到stderr裏的函數。若是「節點」的名字出如今這個環境變量裏,那麼就返回一個功能相似於console.error()的函數.若是不是,那麼返回一個空函數.

例如:

var bar = 123; debuglog('hello from foo [%d]', bar);

<!-- endsection --> <!-- section:841c12a486aeca12985eeae2d550044e --> 若是這個程序以`NODE_DEBUG=foo` 的環境運行,那麼它將會輸出: <!-- endsection --> <!-- section:cefee92825ed4220569779223fcc49f3 --> FOO 3245: hello from foo [123] <!-- endsection --> <!-- section:17ef1e93428ebec32b98fa8fe18e7807 --> `3245`是進程的ID, 若是程序不以剛纔那樣設置的環境變量運行,那麼將不會輸出任何東西。 <!-- endsection --> <!-- section:fb8af07a0bc0e884ec481501fb9ee17d --> 多個`NODE_DEBUG`環境變量,你能夠用逗號進行分割。例如,`NODE_DEBUG= fs, net, tls`。 <!-- endsection --> <!-- section:1785afa5e0b057aea818cd8bc131248a --> ## util.format(format, [...]) <!-- endsection --> <!-- section:91ee7971cb6dbe7c7841d5c0357a625a --> 根據第一個參數,返回一個格式化字符串,相似`printf`的格式化輸出。 <!-- endsection --> <!-- section:eed068a3508e3b9cc687607e97338b9f --> 第一個參數是一個字符串,包含零個或多個*佔位符*。 每個佔位符被替換爲與其對應的轉換後的值。 支持的佔位符有: <!-- endsection --> <!-- section:da9b014604572a67a757e892ddd36dd3 --> * `%s` - 字符串. * `%d` - 數字 (整型和浮點型). * `%j` - JSON. 若是這個參數包含循環對象的引用,將會被替換成字符串 `'[Circular]'`。 * `%%` - 單獨一個百分號(`'%'`)。不會消耗一個參數。 <!-- endsection --> <!-- section:c2996cb0cabc702cd8d2bf9d2410599b --> 若是佔位符沒有相對應的參數,佔位符將不會被替換。 <!-- endsection --> <!-- section:8b6a9f474a82c2495887bf9fe6602308 --> util.format('%s:%s', 'foo'); // 'foo:%s' <!-- endsection --> <!-- section:1e95291ba804f022549694a216ac10c6 --> 若是有多個參數佔位符,額外的參數將會調用`util.inspect()`轉換爲字符串。這些字符串被鏈接在一塊兒,而且以空格分隔。 <!-- endsection --> <!-- section:ba9ecf5f8b441a986c81bee295e7f5f6 --> util.format('%s:%s', 'foo', 'bar', 'baz'); // 'foo:bar baz' <!-- endsection --> <!-- section:f34f71da627ffc546bb170512e2ffa99 --> 若是第一個參數是一個非格式化字符串,那麼`util.format()`將會把全部的參數轉成字符串,以空格隔開,拼接在一塊,並返回該字符串。`util.inspect()`會把每一個參數都轉成一個字符串。 <!-- endsection --> <!-- section:25dcc02c2222a4275231d90579c8598e --> util.format(1, 2, 3); // '1 2 3' <!-- endsection --> <!-- section:f695fcc4f18f5c6e339ccce4e3389dcf --> ## util.log(string) <!-- endsection --> <!-- section:726d139874d57d83bf5da6c193940e4d --> 在控制檯進行輸出,並帶有時間戳。 <!-- endsection --> <!-- section:ba5cd1d0dee4eb1d7de5808ac9c816b8 --> 示例:require('util').log('Timestamped message.'); <!-- endsection --> <!-- section:49290ff385e98b889e7199f35d8fdd82 --> ## util.inspect(object, [options]) <!-- endsection --> <!-- section:1109f4d3eef77037e85144a6ac35edb4 --> 返回一個對象的字符串表現形式, 在代碼調試的時候很是有用. <!-- endsection --> <!-- section:258dc184e5a4c8130e86a263acb47331 --> 能夠經過加入一些可選選項,來改變對象的格式化輸出形式: <!-- endsection --> <!-- section:e8f8d3d45f5ea0449c8466bcc7d1aef5 --> - `showHidden` - 若是設爲 `true`,那麼該對象的不可枚舉的屬性將會被顯示出來。默認爲`false`. <!-- endsection --> <!-- section:961283759790f15496ef9cfd71e80da0 --> - `depth` - 告訴 `inspect` 格式化對象的時候遞歸多少次。這個選項在格式化複雜對象的時候比較有用。 默認爲 `2`。若是想無窮遞歸下去,則賦值爲`null`便可。 <!-- endsection --> <!-- section:87002282463d3e4ae824462785cdb97f --> - `colors` - 若是設爲`true`,將會以`ANSI`顏色代碼風格進行輸出. 默認是`false`。顏色是可定製的,請看下面: <!-- endsection --> <!-- section:e58f864e5d78935059b0d82a0de99ac5 --> - `customInspect` - 若是設爲 `false`,那麼定義在被檢查對象上的`inspect(depth, opts)` 方法將不會被調用。 默認爲`true`。 <!-- endsection --> <!-- section:ba2e5c5a48d29e5af4a12604012adbed --> 示例:檢查`util`對象上的全部屬性 <!-- endsection --> <!-- section:b18c78721710be6505a368795090fa2a --> console.log(util.inspect(util, { showHidden: true, depth: null })); <!-- endsection --> <!-- section:861c779d7e06e5793e09df841bcb961e --> 當被調用的時候,參數值能夠提供本身的自定義`inspect(depth, opts)`方法。該方法會接收當前的遞歸檢查深度,以及傳入`util.inspect()`的其餘參數。 <!-- endsection --> <!-- section:4a6133be34b7ea241e0ca2c1f1352b67 --> ### 自定義 `util.inspect` 顏色 <!-- endsection --> <!-- type=misc --> <!-- section:e67331593b47e5b33c512bdb1b0ff48a --> `util.inspect`彩色輸出(若是啓用的話) ,能夠經過`util.inspect.styles` 和 `util.inspect.colors` 來全局定義。 <!-- endsection --> <!-- section:a54599adbca30a4d73b7f925966c5603 --> `util.inspect.styles`是經過`util.inspect.colors`分配給每一個風格顏色的一個映射。 高亮風格和它們的默認值: * `number` (黃色) * `boolean` (黃色) * `string` (綠色) * `date` (洋紅色) * `regexp` (紅色) * `null` (粗體) * `undefined` (灰色) * `special` - 在這個時候的惟一方法 (青綠色) * `name` (無風格) <!-- endsection --> <!-- section:3ee112e8f48b058b8ad29568e75ef607 --> 預約義的顏色代碼: `white`, `grey`, `black`, `blue`, `cyan`, `green`, `magenta`, `red` 和 `yellow`。 還有 `bold`, `italic`, `underline` 和 `inverse` 代碼。 <!-- endsection --> <!-- section:440be6fb8aa71354fef5dea7fc96943d --> ### 自定義對象的`inspect()`方法 <!-- endsection --> <!-- type=misc --> <!-- section:3e669407dab4df9d4b8bb939bd6a2ae8 --> 對象能夠定義本身的 `inspect(depth)`方法;當使用`util.inspect()`檢查該對象的時候,將會執行對象自定義的檢查方法。 <!-- endsection --> <!-- section:2f67cef2dc14fcc47a091e9b18eda982 --> util.inspect(obj); // "{nate}" <!-- endsection --> <!-- section:53e8daad6eb7817fc7dd12eec07bcb31 --> 您也能夠返回徹底不一樣的另外一個對象,並且返回的字符串將被根據返回的對象格式化。它和`JSON.stringify()`工做原理相似: <!-- endsection --> <!-- section:237e76b838a128a2faf99212684fa8b9 --> util.inspect(obj); // "{ bar: 'baz' }" <!-- endsection --> <!-- section:f7540c4eb10d670a568d050db1ea2bb5 --> ## util.isArray(object) <!-- endsection --> <!-- section:ea6ec473af78d910796c4ff2f4beba6d --> 若是給定的對象是`數組`類型,就返回`true`,不然返回`false` <!-- endsection --> <!-- section:d54b11adadfb753c5dc492ca11c2be6c --> util.isArray([]) // true util.isArray(new Array) // true util.isArray({}) // false <!-- endsection --> <!-- section:d93077c7b296f1169872fefca84c9be9 --> ## util.isRegExp(object) <!-- endsection --> <!-- section:4e189cb02b2cbad06d09d47e35f806cb --> 若是給定的對象是`RegExp`類型,就返回`true`,不然返回`false`。 <!-- endsection --> <!-- section:8d9463d7c3b441728cdaee8c6995c91e --> util.isRegExp(/some regexp/) // true util.isRegExp(new RegExp('another regexp')) // true util.isRegExp({}) // false <!-- endsection --> <!-- section:931161fabda5cc5e34b8b244fa3a1739 --> ## util.isDate(object) <!-- endsection --> <!-- section:0e10fe8824ec08d1a4604ec484b4ee75 --> 若是給定的對象是`Date`類型,就返回`true`,不然返回`false`。 <!-- endsection --> <!-- section:a18b5ed95ee84e4dd2f4f0edce689916 --> util.isDate(new Date()) // true util.isDate(Date()) // false (沒有關鍵字 'new' 返回一個字符串) util.isDate({}) // false <!-- endsection --> <!-- section:8d36aedca50abcc1df1990d2e2bee19a --> ## util.isError(object) <!-- endsection --> <!-- section:4e7d0ccda0f08bf4e2499e2529303237 --> 若是給定的對象是`Error`類型,就返回`true`,不然返回`false`。 <!-- endsection --> <!-- section:e2ac9e8dc03345e5cb9a55f6a9d340f3 --> util.isError(new Error()) // true util.isError(new TypeError()) // true util.isError({ name: 'Error', message: 'an error occurred' }) // false <!-- endsection --> <!-- section:81fdd6c58be9d08b05ff8f7756b39195 --> ## util.inherits(constructor, superConstructor) <!-- endsection --> <!-- section:4477c32b0656b2610fd0d5967137862f --> 經過[構造函數](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/constructor),繼承原型對象上的方法。構造函數的`原型`將被設置爲一個新的 從`超類`建立的對象。 <!-- endsection --> <!-- section:d27d3ddf50111db4f9c6bf7796b6c7cd --> 你能夠很方便的經過 `constructor.super_`來訪問到`superConstructor` <!-- endsection --> <!-- section:bb768700b523e98319d92c95c8f3c055 --> stream.on("data", function(data) { console.log('Received data: "' + data + '"'); }) stream.write("It works!"); // 輸出結果:Received data: "It works!" <!-- endsection --> <!-- section:a3f76941a46977219d729ec32e53c169 --> ## util.debug(string) <!-- endsection --> <!-- section:082abafa227eefa4d2d293287dde4ffc --> 穩定度: 0 - 已過期: 請使用 console.error() 代替 <!-- endsection --> <!-- section:70aa4fdc0ad82574f81cea79a9524c49 --> `console.error`的已過期的前身 <!-- endsection --> <!-- section:107d1fd1ab87080f18dfba53fe1973d0 --> ## util.error([...]) <!-- endsection --> <!-- section:8470c9173b3022a3aaa5c99b573d1213 --> 穩定度: 0 - 已過期: 請使用 console.error() 代替 <!-- endsection --> <!-- section:70aa4fdc0ad82574f81cea79a9524c49 --> `console.error`的已過期的前身 <!-- endsection --> <!-- section:14669ebe2f4fa3a034d5c8324b84ba0e --> ## util.puts([...]) <!-- endsection --> <!-- section:292d0ba9e4d38fca3ad39db7b0576fc4 --> 穩定度: 0 - 已過期: 請使用 console.log() 代替 <!-- endsection --> <!-- section:fc6c6925ce3665fbb2e48ca90bb64632 --> `console.log`的已過期的前身 <!-- endsection --> <!-- section:65a5b8c03e11e172f9755a1f81e3a745 --> ## util.print([...]) <!-- endsection --> <!-- section:34d7799b56a10685b5e6afa53049eb41 --> 穩定度: 0 - 已過期: 請使用 console.log() 代替 <!-- endsection --> <!-- section:fc6c6925ce3665fbb2e48ca90bb64632 --> `console.log`的已過期的前身 <!-- endsection --> <!-- section:11c756a231f455e357545d47573db5aa --> ## util.pump(readableStream, writableStream, [callback]) <!-- endsection --> <!-- section:697b95943d8e32d1b3c8126d4c2464d3 --> 穩定度: 0 - 已過期: 請使用readableStream.pipe(writableStream)代替 <!-- endsection --> <!-- section:d10aa97ce6544f151e627e28f34da150 --> `stream.pipe()`的已過期的前身 <!-- endsection --> <!-- section:50067abc5ffa11c9b47cc886357867f3 --> # 事件 (Events) <!-- endsection --> <!-- section:050a2049458b1f3dbf3e3f111adcfb22 --> 穩定度: 4 - 凍結 <!-- endsection --> <!--type=module--> <!-- section:7f7a3ddc542d9af4dc52c2424c9b3372 --> Node裏面的許多對象都會分發事件:一個`net.Server`對象會在每次有新鏈接時分發一個事件, 一個`fs.readStream`對象會在文件被打開的時候發出一個事件。 全部這些產生事件的對象都是 `events.EventEmitter` 的實例。 你能夠經過`require("events");`來訪問該模塊 <!-- endsection --> <!-- section:6d51594acab94889caec68537bcbda17 --> 一般,事件名是駝峯命名 (camel-cased) 的字符串。不過也沒有強制的要求,任何字符串都是能夠使用的。 <!-- endsection --> <!-- section:e4b58d5a58cc1f95ee6e57ac0c291aab --> 爲了處理髮出的事件,咱們將函數 (Function) 關聯到對象上。 咱們把這些函數稱爲 _監聽器 (listeners)_。 在監聽函數中 `this` 指向當前監聽函數所關聯的 `EventEmitter` 對象。 <!-- endsection --> <!-- section:2b8a6e164096f811fa8bf664e7e472c7 --> ## 類: events.EventEmitter <!-- endsection --> <!-- section:e645ea46cb2c41306cbe95cedc37e779 --> 經過 `require('events').EventEmitter` 獲取 EventEmitter 類。 <!-- endsection --> <!-- section:2055e28a1e68af4b8eccb472c83c9405 --> 當 `EventEmitter` 實例遇到錯誤,一般的處理方法是產生一個 `'error'` 事件,node 對錯誤事件作特殊處理。 若是程序沒有監聽錯誤事件,程序會按照默認行爲在打印出 棧追蹤信息 (stack trace) 後退出。 <!-- endsection --> <!-- section:ed8b0d43e3f5764e2e59d882b03e9455 --> EventEmitter 會在添加 listener 時觸發 `'newListener'` 事件,刪除 listener 時觸發 `'removeListener'` 事件 <!-- endsection --> <!-- section:913dcf12a24c31ab548b614f25f45423 --> ### emitter.addListener(event, listener) ### emitter.on(event, listener) <!-- endsection --> <!-- section:a67bc20697622a368255913731414b90 --> 添加一個 listener 至特定事件的 listener 數組尾部。 <!-- endsection --> <!-- section:19dcc396a5e6e364ee6fdc2ac4d9ce91 --> server.on('connection', function (stream) { console.log('someone connected!'); }); <!-- endsection --> <!-- section:f8a794bb5a470673ac332f06e02697b0 --> 返回 emitter,方便鏈式調用。 <!-- endsection --> <!-- section:8337b32ac40477f65b612c80b9ff98f5 --> ### emitter.once(event, listener) <!-- endsection --> <!-- section:7bc0510dd38534106967c6989bb37f65 --> 添加一個 **一次性** listener,這個 listener 只會在下一次事件發生時被觸發一次,觸發完成後就被刪除。 <!-- endsection --> <!-- section:e2ce327df5ffc2bc634f8ad41b64736f --> server.once('connection', function (stream) { console.log('Ah, we have our first user!'); }); <!-- endsection --> <!-- section:f8a794bb5a470673ac332f06e02697b0 --> 返回 emitter,方便鏈式調用。 <!-- endsection --> <!-- section:d9f647871d1a7bbb46b69321cfd81c08 --> ### emitter.removeListener(event, listener) <!-- endsection --> <!-- section:af2e0a30ab565a277fa00e119ae7822e --> 從一個事件的 listener 數組中刪除一個 listener **注意**:此操做會改變 listener 數組中在當前 listener 後的listener 的位置下標 <!-- endsection --> <!-- section:990e0558498e767e51f7ea0c0c5cb042 --> var callback = function(stream) { console.log('someone connected!'); }; server.on('connection', callback); // ... server.removeListener('connection', callback); <!-- endsection --> <!-- section:f8a794bb5a470673ac332f06e02697b0 --> 返回 emitter,方便鏈式調用。 <!-- endsection --> <!-- section:4d8352cda5b3eb935d3152752ba71c91 --> ### emitter.removeAllListeners([event]) <!-- endsection --> <!-- section:a339aa62a2f3910e917b30c2a51f60b9 --> 刪除全部 listener,或者刪除某些事件 (event) 的 listener <!-- endsection --> <!-- section:f8a794bb5a470673ac332f06e02697b0 --> 返回 emitter,方便鏈式調用。 <!-- endsection --> <!-- section:a36c89f2ca78381d065f3bc64f63c4af --> ### emitter.setMaxListeners(n) <!-- endsection --> <!-- section:6144a744b6f9ee4d42048fd453b00cb8 --> 在默認狀況下,EventEmitter 會在多於 10 個 listener 監聽某個事件的時候出現警告,此限制在尋找內存泄露時很是有用。 顯然,也不是全部的 Emitter 事件都要被限制在 10 個 listener 如下,在這種狀況下能夠使用這個函數來改變這個限制。設置0這樣能夠沒有限制。 <!-- endsection --> <!-- section:f8a794bb5a470673ac332f06e02697b0 --> 返回 emitter,方便鏈式調用。 <!-- endsection --> <!-- section:3cbb5b1cdc26b98048d02ee59e200085 --> ### EventEmitter.defaultMaxListeners <!-- endsection --> <!-- section:3f8697f74741fb77dc105c396c369118 --> `emitter.setMaxListeners(n)` 設置每一個 emitter 實例的最大監聽數。 這個類屬性爲 **全部** `EventEmitter` 實例設置最大監聽數(對全部已建立的實例和從此建立的實例都將當即生效)。 使用時請注意。 <!-- endsection --> <!-- section:ade73e4b7792e2b08162712ddbbbf6a6 --> 請注意,`emitter.setMaxListeners(n)` 優先於 `EventEmitter.defaultMaxListeners`。 <!-- endsection --> <!-- section:ba73b4320fe912daf900d9b75575b092 --> ### emitter.listeners(event) <!-- endsection --> <!-- section:f118af568a602d164a3c9bc8abd2f113 --> 返回指定事件的 listener 數組 <!-- endsection --> <!-- section:b9fc5400d3bf978204af2becda716c84 --> server.on('connection', function (stream) { console.log('someone connected!'); }); console.log(util.inspect(server.listeners('connection'))); // [ [Function] ] <!-- endsection --> <!-- section:c7e909a1c6559cf990730664daa95727 --> ### emitter.emit(event, [arg1], [arg2], [...]) <!-- endsection --> <!-- section:32a896a1670303a11ddf018be68a05b8 --> 使用提供的參數按順序執行指定事件的 listener <!-- endsection --> <!-- section:97f7fcb289631ff8b970f572f5fc23a3 --> 若事件有 listeners 則返回 `true` 不然返回 `false`。 <!-- endsection --> <!-- section:a6b3da0a0acb015accda9106a1ed5914 --> ### 類方法: EventEmitter.listenerCount(emitter, event) <!-- endsection --> <!-- section:f5c8597bec06973f818d385f56871f57 --> 返回指定事件的 listeners 個數 <!-- endsection --> <!-- section:a7c55b76049b86734b2943f83acc833f --> ### 事件: 'newListener' <!-- endsection --> <!-- section:a8e2a4f5abdec4d8a52bacf1dee9f2e7 --> * `event` {String} 事件名 * `listener` {Function} 事件處理函數 <!-- endsection --> <!-- section:7815a60313e081a7ba22c61f63437710 --> 在添加 listener 時會發生該事件。 此時沒法肯定 `listener` 是否在 `emitter.listeners(event)` 返回的列表中。 <!-- endsection --> <!-- section:689154e61bde2e8fc9df9e19d1139ec6 --> ### 事件: 'removeListener' <!-- endsection --> <!-- section:a8e2a4f5abdec4d8a52bacf1dee9f2e7 --> * `event` {String} 事件名 * `listener` {Function} 事件處理函數 <!-- endsection --> <!-- section:72d198d24c91e07c4927b53c4b2fa2f5 --> 在移除 listener 時會發生該事件。 此時沒法肯定 `listener` 是否在 `emitter.listeners(event)` 返回的列表中。 <!-- endsection --> <!-- section:02cca00571635b304013bb0f1929d602 --> # 域 <!-- endsection --> <!-- section:14ae3b8a1560651cf34fa2e5562e7f27 --> 穩定度: 2 - 不穩定 <!-- endsection --> <!-- section:4518ebd75856bc13fa183ae778a0f8cf --> Domains 提供了一種方式,即以一個單一的組的形式來處理多個不一樣的IO操做。若是任何一個註冊到domain的事件觸發器或回調觸發了一個‘error’事件,或者拋出一個錯誤,那麼domain對象將會被通知到。而不是直接讓這個錯誤的上下文從`process.on('uncaughtException')'處理程序中丟失掉,也不會導致程序由於這個錯誤伴隨着錯誤碼當即退出。 <!-- endsection --> <!-- section:9f7e65c02ceb7847e9d1b089fd9045db --> ## 警告: 不要忽視錯誤! <!-- endsection --> <!-- type=misc --> <!-- section:0e9443611b77944922566c8d53e84ffb --> Domain error處理程序不是一個在錯誤發生時,關閉你的進程的替代品 <!-- endsection --> <!-- section:d2e33c9dd4cbc57096f58347b126fd5e --> 基於'拋出(throw)'在JavaScript中工做的方式,幾乎歷來沒有任何方式可以在‘不泄露引用,不形成一些其餘種類的未定義的脆弱狀態’的前提下,安全的「從你離開的地方從新拾起(pick up where you left off)」, <!-- endsection --> <!-- section:48116c9b8f54166db432d18a336ea5ed --> 響應一個被拋出錯誤的最安全方式就是關閉進程。固然,在一個正常的Web服務器中,你可能會有不少活躍的鏈接。因爲其餘觸發的錯誤你去忽然關閉這些鏈接是不合理。 <!-- endsection --> <!-- section:31e72d6174069a5067433645717c9973 --> 更好的方法是發送錯誤響應給那個觸發錯誤的請求,在保證其餘人正常完成工做時,中止監聽那個觸發錯誤的人的新請求。 <!-- endsection --> <!-- section:3bc705aac660bdeab33e0469f620025f --> 在這種方式中,`域`使用伴隨着集羣模塊,因爲主過程能夠叉新工人時,一個工人發生了一個錯誤。節點程序規模的多 機,終止代理或服務註冊能夠注意一下失敗,並作出相應的反應。 <!-- endsection --> <!-- section:697acd8be310c559e6f31ebfef28a638 --> 舉例來講,如下就不是一個好想法: <!-- endsection --> <!-- section:3b5ceef23f6efb9bf6390967ac13d044 --> var d = require('domain').create(); d.on('error', function(er) {  // 這個錯誤不會致使進程崩潰,可是狀況會更糟糕!  // 雖然咱們阻止了進程忽然重啓動,可是咱們已經發生了資源泄露  // 這種事情的發生會讓咱們發瘋。  // 不如調用 process.on('uncaughtException')!  console.log('error, but oh well', er.message); }); d.run(function() {  require('http').createServer(function(req, res) {  handleRequest(req, res);  }).listen(PORT); });

經過對域的上下文的使用,以及將咱們的程序分隔成多個工做進程的反射,咱們能夠作出更加恰當的反應和更加安全的處理。

// 好一些的作法! <!-- endsection --> <!-- section:1002b470aa3001d212344187000af555 --> var cluster = require('cluster'); var PORT = +process.env.PORT || 1337; <!-- endsection --> <!-- section:2f82bd01f23b2a29fbda6b4d88878ba5 --> if (cluster.isMaster) { // 在工做環境中,你可能會使用到不止一個工做分支 // 並且可能不會把主幹和分支放在同一個文件中 // //你固然能夠經過日誌進行猜想,而且對你須要防止的DoS攻擊等不良行爲實施自定義的邏輯 // // 看集羣文件的選項 // // 最重要的是主幹很是小,增長了咱們抵抗之外錯誤的可能性。 <!-- endsection --> <!-- section:cb647d887934b4f503df68aec3e5bb82 --> cluster.fork(); cluster.fork(); <!-- endsection --> <!-- section:73e4dad498a584605744c08e8889acd2 --> cluster.on('disconnect', function(worker) { console.error('disconnect!'); cluster.fork(); }); <!-- endsection --> <!-- section:1eb9320833f0f4735c2a02437b831763 --> } else { // 工做進程 // // 這是咱們出錯的地方 <!-- endsection --> <!-- section:4e5957024feac866ed9559ec321d9348 --> var domain = require('domain'); <!-- endsection --> <!-- section:3eb2409b6e7d143f8eea5a1e547278b1 --> //看集羣文件對於使用工做進程處理請求的更多細節,它是如何工做的,它的警告等等。 <!-- endsection --> <!-- section:dd3bdd23463afb9f443cd297ec716fb5 --> var server = require('http').createServer(function(req, res) { var d = domain.create(); d.on('error', function(er) { console.error('error', er.stack); <!-- endsection --> <!-- section:e614fa9e1583991a0d9b352d554182ba --> // 由於req和res在這個域存在以前就被建立, // 因此咱們須要顯式添加它們。 // 詳見下面關於顯式和隱式綁定的解釋。 d.add(req); d.add(res); <!-- endsection --> <!-- section:ec427556232abea87deeffa7ae5a5830 --> // 如今在域裏面運行處理器函數。 d.run(function() { handleRequest(req, res); }); }); server.listen(PORT); } <!-- endsection --> <!-- section:8792f2c8a87d64cd7a3893d4f03b9c89 --> // 這個部分不是很重要。只是一個簡單的路由例子。 // 你會想把你的超級給力的應用邏輯放在這裏。 function handleRequest(req, res) { switch(req.url) { case '/error': // 咱們幹了一些異步的東西,而後。。。 setTimeout(function() { // 呃。。。 flerb.bark(); }); break; default: res.end('ok'); } }

對Error(錯誤)對象的內容添加#

每一次一個Error對象被導向通過一個域,它會添加幾個新的字段。

  • error.domain 第一個處理這個錯誤的域。
  • error.domainEmitter 用這個錯誤對象觸發'error'事件的事件分發器。
  • error.domainBound 回調函數,該回調函數被綁定到域,而且一個錯誤會做爲第一參數傳遞給這個回調函數。
  • error.domainThrown 一個布爾值代表這個錯誤是否被拋出,分發或者傳遞給一個綁定的回調函數。

隱式綁定#

若是多個域正在被使用,那麼全部的EventEmitter對象(包括Stream對象,請求,應答等等)會被隱式綁定到它們被建立時的有效域。

並且,被傳遞到低層事件分發請求的回調函數(例如fs.open,或者其它接受回調函數的函數)會自動綁定到有效域。若是這些回調函數拋出錯誤,那麼這個域會捕捉到這個錯誤。

爲了防止內存的過分使用,Domain對象本身不會做爲有效域的子對象被隱式添加到有效域。由於若是這樣作的話,會很容易影響到請求和應答對象的正常垃圾回收。

若是你在一個父Domain對象裏嵌套子Domain對象,那麼你須要顯式地添加它們。

隱式綁定將被拋出的錯誤和'error'事件導向到Domain對象的error事件,但不會註冊到Domain對象上的EventEmitter對象,因此domain.dispose()不會令EventEmitter對象中止運做。隱式綁定只關心被拋出的錯誤和 'error'事件。

顯式綁定#

有時,正在使用的域並非某個事件分發器所應屬的域。又或者,事件分發器在一個域內被建立,可是應該被綁定到另外一個域。

例如,對於一個HTTP服務器,能夠有一個正在使用的域,但咱們可能但願對每個請求使用一個不一樣的域。

這能夠經過顯示綁定來達到。

例如:

serverDomain.run(function() { // 服務器在serverDomain的做用域內被建立 http.createServer(function(req, res) { // req和res一樣在serverDomain的做用域內被建立 // 可是,咱們想對於每個請求使用一個不同的域。 // 因此咱們首先建立一個域,而後將req和res添加到這個域上。 var reqd = domain.create(); reqd.add(req); reqd.add(res); reqd.on('error', function(er) { console.error('Error', er, req.url); try { res.writeHead(500); res.end('Error occurred, sorry.'); } catch (er) { console.error('Error sending 500', er, req.url); } }); }).listen(1337); }); ```

domain.create()#

  • return: {Domain}

返回一個新的Domain對象。

類: Domain#

Domain類封裝了將錯誤和沒有被捕捉的異常導向到有效對象的功能。

Domain是 EventEmitter類的一個子類。監聽它的error事件來處理它捕捉到的錯誤。

domain.run(fn)#

  • fn {Function}

在域的上下文裏運行提供的函數,隱式地綁定全部該上下文裏建立的事件分發器,計時器和低層請求。

這是使用一個域的最基本的方式。

示例:

var d = domain.create(); d.on('error', function(er) { console.error('Caught error!', er); }); d.run(function() { process.nextTick(function() { setTimeout(function() { // 模擬幾個不一樣的異步的東西 fs.open('non-existent file', 'r', function(er, fd) { if (er) throw er; // 繼續。。。 }); }, 100); }); });

在這個例子裏, d.on('error') 處理器會被觸發,而不是致使程序崩潰。

domain.members#

  • {Array}

一個數組,裏面的元素是被顯式添加到域裏的計時器和事件分發器。

domain.add(emitter)#

  • emitter {EventEmitter | Timer} 被添加到域裏的時間分發器或計時器

顯式地將一個分發器添加到域。若是這個分發器調用的任意一個事件處理器拋出一個錯誤,或是這個分發器分發了一個error事,那麼它會被導向到這個域的error事件,就像隱式綁定所作的同樣。

這對於從setIntervalsetTimeout返回的計時器一樣適用。若是這些計時器的回調函數拋出錯誤,它將會被這個域的error處理器捕捉到。

若是這個Timer或EventEmitter對象已經被綁定到另一個域,那麼它將會從那個域被移除,而後綁定到當前的域。

domain.remove(emitter)#

  • emitter {EventEmitter | Timer} 要從域裏被移除的分發器或計時器

domain.add(emitter)函數偏偏相反,這個函數將域處理從指明的分發器裏移除。

domain.bind(callback)#

  • callback {Function} 回調函數
  • return: {Function} 被綁定的函數

返回的函數會是一個對於所提供的回調函數的包裝函數。當這個被返回的函數被調用時,全部被拋出的錯誤都會被導向到這個域的error事件。

例子#

d.on('error', function(er) { // 有個地方發生了一個錯誤。 // 若是咱們如今拋出這個錯誤,它會讓整個程序崩潰 // 並給出行號和棧信息。 });

domain.intercept(callback)#

  • callback {Function} 回調函數
  • return: {Function} 被攔截的函數

這個函數與domain.bind(callback)幾乎如出一轍。可是,除了捕捉被拋出的錯誤外,它還會攔截做爲第一參數被傳遞到這個函數的Error對象。

在這種方式下,常見的'if(er) return callback(er);'的方式能夠被一個單獨地方的單獨的錯誤處理所取代。

例子#

d.on('error', function(er) { // 有個地方發生了一個錯誤。 // 若是咱們如今拋出這個錯誤,它會讓整個程序崩潰 // 並給出行號和棧信息。 });

domain.enter()#

enter函數對於runbindintercept來講就像它們的管道系統:它們使用enter函數來設置有效域。enter函數對於域設定了domain.active和 process.domain ,還隱式地將域推入了由域模塊管理的域棧(關於域棧的細節詳見domain.exit())。enter函數的調用,分隔了異步調用鏈以及綁定到一個域的I/O操做的結束或中斷。

調用enter僅僅改變活動的域,而不改變域自己。 Enter 和 exit在一個單獨的域能夠被調用任意屢次。

若是域的enter已經設置,enter將不設置域就返回。

domain.exit()#

exit函數退出當前的域,將當前域從域的棧裏移除。每當當程序的執行流程準要切換到一個不一樣的異步調用鏈的上下文時,要保證退出當前的域。exit函數的調用,分隔了異步調用鏈以及綁定到一個域的I/O操做的結束或中斷。

若是有多個嵌套的域綁定到當前的執行上下文, 退出將退出在這個域裏的全部的嵌套。

調用exit只會改變有效域,而不會改變域自身。在一個單一域上,Enterexit能夠被調用任意次。

若是在這個域名下exit 已經被設置,exit 將不退出域返回。

domain.dispose()#

穩定度: 0 - 已過期。請經過設置在域上的錯誤事件處理器,顯式地東失敗的IO操做中恢復。

一旦dispose被調用,經過runbindintercept綁定到這個域的回調函數將再也不使用這個域,而且一個dispose事件會被分發。

Buffer#

穩定度: 3 - 穩定

純 JavaScript 對 Unicode 友好可是沒法很好地處理二進制數據。當咱們面對相似 TCP 流或文件系統時,是須要處理八位流的。Node 有幾種操做、建立以及消費八位流的策略。

原始數據保存在 Buffer 類的實例中。一個 Buffer 實例相似於一個整數數組,但對應者 V8 堆以外的一個原始內存分配區域。一個 Buffer 的大小不可變。

Buffer 類是一個全局的類,因此它很罕有地不須要require語句就能夠調用。

在Buffers和JavaScript string轉換時,須要明確的一個編碼方法。下面是一些不一樣的string編碼。

  • 'ascii' - 僅適用 7 bit ASCII 格式數據。這個編碼方式很是快速,並且會剝離設置太高的bit。
  • 'utf8' - 多字節編碼 Unicode字符。不少網頁或者其餘文檔的編碼格式都是使用 UTF-8的。
  • 'utf16le' - 2 或者 4 字節, Little Endian (LE) 編碼Unicode字符。 代理對 (U+10000 to U+10FFFF) 是支持的.(BE和LE表示大端和小端,Little-Endian就是低位字節排放在內存的低地址端,高位字節排放在內存的高地址端;Big-Endian就是高位字節排放在內存的低地址端,低位字節排放在內存的高地址端;下同)
  • 'ucs2' -  'utf16le'的別名.
  • 'base64' - Base64 字符串編碼。
  • 'binary' - 一個將原始2進制數據編碼爲字符串的方法,僅使用每一個字符的前8bits。 這個編碼方式已經被棄用並且應該被避免,儘量的使用Buffer對象。這個編碼方式將會在將來的Node版本中移除。
  • 'hex' - 把每一個byte編碼成2個十六進制字符

類: Buffer#

Buffer 類是一個全局變量類型,用來直接處理2進制數據的。 它可以使用多種方式構建。

new Buffer(size)#

  • size Number

分配一個新的 buffer 大小是 size 的8位字節.

new Buffer(array)#

  • array Array

分配一個新的 buffer 使用一個8位字節 array 數組.

new Buffer(str, [encoding])#

  • str String類型 - 須要存入buffer的string字符串.
  • encoding String類型 - 使用什麼編碼方式,參數可選.

分配一個新的 buffer ,其中包含着給定的 str字符串. encoding 編碼方式默認是:'utf8'.

類方法: Buffer.isEncoding(encoding)#

  • encoding {String} 用來測試給定的編碼字符串

若是給定的編碼 encoding 是有效的,返回 true,不然返回 false。

類方法: Buffer.isBuffer(obj)#

  • obj Object
  • 返回: Boolean

測試這個 obj 是不是一個 Buffer.

類方法: Buffer.byteLength(string, [encoding])#

  • string String類型
  • encoding String類型, 可選參數, 默認是: 'utf8'
  • Return: Number類型

將會返回這個字符串真實byte長度。 encoding 編碼默認是: 'utf8'. 這個和 String.prototype.length是不同的,由於那個方法返回這個字符串中有幾個字符的數量。 (譯者:當用戶在寫http響應頭Cotent-Length的時候,千萬記得必定要用 Buffer.byteLength 方法,不要使用 String.prototype.length )

示例:

// ½ + ¼ = ¾: 9 characters, 12 bytes

類方法: Buffer.concat(list, [totalLength])#

  • list {Array}數組類型,Buffer數組,用於被鏈接。
  • totalLength {Number}類型 上述Buffer數組的全部Buffer的總大小。(譯者:注意這裏的totalLength不是數組長度是數組裏Buffer實例的大小總和)

返回一個保存着將傳入buffer數組中全部buffer對象拼接在一塊兒的buffer對象。(譯者:有點拗口,其實就是將數組中全部的buffer實例經過複製拼接在一塊兒)

若是傳入的數組沒有內容,或者 totalLength 參數是0,那將返回一個zero-length的buffer。

若是數組中只有一項,那麼這第一項就會被返回。

若是數組中的項多於一個,那麼一個新的Buffer實例將被建立。

若是 totalLength 參數沒有提供,雖然會從buffer數組中計算讀取,可是會增長一個額外的循環來計算它,因此提供一個明確的 totalLength 參數將會更快。

buf.length#

  • Number類型

這個buffer的bytes大小。注意這未必是這buffer裏面內容的大小。length 的依據是buffer對象所分配的內存數值,它不會隨着這個buffer對象內容的改變而改變。

// 1234 // 1234

buf.write(string, [offset], [length], [encoding])#

  • string String類型 - 將要被寫入 buffer 的數據
  • offset Number類型, 可選參數, 默認: 0
  • length Number類型, 可選參數, 默認: buffer.length - offset
  • encoding String類型, 可選參數, 默認: 'utf8'

根據參數 offset 偏移量和指定的encoding編碼方式,將參數 string 數據寫入buffer。 offset偏移量 默認是 0encoding編碼方式默認是 'utf8'。 length長度是將要寫入的字符串的bytes大小。 返回number類型,表示多少8位字節流被寫入了。若是buffer 沒有足夠的空間來放入整個string,它將只會寫入部分的字符串。 length 默認是 buffer.length - offset。 這個方法不會出現寫入部分字符。

buf = new Buffer(256); len = buf.write('\u00bd + \u00bc = \u00be', 0); console.log(len + " bytes: " + buf.toString('utf8', 0, len));

buf.toString([encoding], [start], [end])#

  • encoding String類型, 可選參數, 默認: 'utf8'
  • start Number類型, 可選參數, 默認: 0
  • end Number類型, 可選參數, 默認: buffer.length

根據 encoding參數(默認是 'utf8')返回一個解碼的 string 類型。還會根據傳入的參數 start (默認是0) 和 end (默認是 buffer.length)做爲取值範圍。

查看上面buffer.write() 的例子.

buf.toJSON()#

返回一個 JSON表示的Buffer實例。JSON.stringify將會默認調用來字符串序列化這個Buffer實例。

示例:

console.log(copy); // <Buffer 74 65 73 74>

buf[index]#

獲取或者設置在指定index索引位置的8位字節。這個值是指單個字節,因此這個值必須在合法的範圍,16進制的0x00 到0xFF,或者0 到255

例子: 拷貝一個 ASCII 編碼的 string 字符串到一個 buffer, 一次一個 byte 進行拷貝:

// node.js

buf.copy(targetBuffer, [targetStart], [sourceStart], [sourceEnd])#

  • targetBuffer Buffer 類型對象 - 將要進行拷貝的Buffer
  • targetStart Number類型, 可選參數, 默認: 0
  • sourceStart Number類型, 可選參數, 默認: 0
  • sourceEnd Number類型, 可選參數, 默認: buffer.length

進行buffer的拷貝,源和目標能夠是重疊的。 targetStart 目標開始偏移 和sourceStart源開始偏移 默認都是 0sourceEnd 源結束位置偏移默認是源的長度 buffer.length.

若是傳遞的值是undefined/NaN 或者是 out of bounds 超越邊界的,就將設置爲他們的默認值。(譯者:這個默認值下面有的例子有說明)

例子: 建立2個Buffer,而後把將buf1的16位到19位 拷貝到 buf2中,而且從buf2的第8位開始拷貝。

// !!!!!!!!qrst!!!!!!!!!!!!!

buf.slice([start], [end])#

  • start Number類型, 可選參數, 默認: 0
  • end Number類型, 可選參數, 默認: buffer.length

返回一個新的buffer,這個buffer將會和老的buffer引用相同的內存地址,只是根據 start (默認是 0) 和end(默認是buffer.length) 偏移和裁剪了索引。 負的索引是從buffer尾部開始計算的。

修改這個新的buffer實例slice切片,也會改變原來的buffer

例子: 建立一個ASCII 字母的 Buffer,對它slice切片,而後修改源Buffer上的一個byte。

// abc // !bc

buf.readUInt8(offset, [noAssert])#

  • offset Number類型
  • noAssert Boolean類型, 可選參數, 默認: false
  • Return: Number類型

從這個buffer對象裏,根據指定的偏移量,讀取一個 unsigned 8 bit integer整形。 

設置參數 noAssert爲true表示忽略驗證offset偏移量參數。 這意味着 offset可能會超出buffer的末尾。默認是 false

示例:

// 0x3 // 0x4 // 0x23 // 0x42

buf.readUInt16LE(offset, [noAssert])#

buf.readUInt16BE(offset, [noAssert])#

  • offset Number類型
  • noAssert Boolean類型, 可選參數, 默認: false
  • Return: Number類型

從這個buffer對象裏,根據指定的偏移量,使用特殊的 endian字節序格式讀取一個 unsigned 16 bit integer。

設置參數 noAssert爲true表示忽略驗證offset偏移量參數。 這意味着 offset可能會超出buffer的末尾。默認是 false

示例:

// 0x0304 // 0x0403 // 0x0423 // 0x2304 // 0x2342 // 0x4223

buf.readUInt32LE(offset, [noAssert])#

buf.readUInt32BE(offset, [noAssert])#

  • offset Number類型
  • noAssert Boolean類型, 可選參數, 默認: false
  • Return: Number類型

從這個buffer對象裏,根據指定的偏移量,使用指定的 endian字節序格式讀取一個 unsigned 32 bit integer。

設置參數 noAssert爲true表示忽略驗證offset偏移量參數。 這意味着 offset可能會超出buffer的末尾。默認是 false

示例:

// 0x03042342 // 0x42230403

buf.readInt8(offset, [noAssert])#

  • offset Number類型
  • noAssert Boolean類型, 可選參數, 默認: false
  • Return: Number類型

從這個buffer對象裏,根據指定的偏移量,讀取一個 signed 8 bit integer。

設置參數 noAssert爲true表示忽略驗證offset偏移量參數。 這意味着 offset可能會超出buffer的末尾。默認是 false

和 buffer.readUInt8同樣的返回,除非buffer中包含了有做爲2的補碼的有符號值。

buf.readInt16LE(offset, [noAssert])#

buf.readInt16BE(offset, [noAssert])#

  • offset Number類型
  • noAssert Boolean類型, 可選參數, 默認: false
  • Return: Number類型

從這個buffer對象裏,根據指定的偏移量,使用特殊的 endian字節序格式讀取一個 signed 16 bit integer。

設置參數 noAssert爲true表示忽略驗證offset偏移量參數。 這意味着 offset可能會超出buffer的末尾。默認是 false

和 buffer.readUInt16同樣返回,除非buffer中包含了有做爲2的補碼的有符號值。

buf.readInt32LE(offset, [noAssert])#

buf.readInt32BE(offset, [noAssert])#

  • offset Number類型
  • noAssert Boolean類型, 可選參數, 默認: false
  • Return: Number類型

從這個buffer對象裏,根據指定的偏移量,使用指定的 endian字節序格式讀取一個 signed 32 bit integer。

設置參數 noAssert爲true表示忽略驗證offset偏移量參數。 這意味着 offset可能會超出buffer的末尾。默認是 false

和 buffer.readUInt32同樣返回,除非buffer中包含了有做爲2的補碼的有符號值。

buf.readFloatLE(offset, [noAssert])#

buf.readFloatBE(offset, [noAssert])#

  • offset Number類型
  • noAssert Boolean類型, 可選參數, 默認: false
  • Return: Number類型

從這個buffer對象裏,根據指定的偏移量,使用指定的 endian字節序格式讀取一個 32 bit float。

設置參數 noAssert爲true表示忽略驗證offset偏移量參數。 這意味着 offset可能會超出buffer的末尾。默認是 false

示例:

// 0x01

buf.readDoubleLE(offset, [noAssert])#

buf.readDoubleBE(offset, [noAssert])#

  • offset Number類型
  • noAssert Boolean類型, 可選參數, 默認: false
  • Return: Number類型

從這個buffer對象裏,根據指定的偏移量,使用指定的 endian字節序格式讀取一個 64 bit double。

設置參數 noAssert爲true表示忽略驗證offset偏移量參數。 這意味着 offset可能會超出buffer的末尾。默認是 false

示例:

// 0.3333333333333333

buf.writeUInt8(value, offset, [noAssert])#

  • value Number類型
  • offset Number類型
  • noAssert Boolean類型, 可選參數, 默認: false

根據指定的offset偏移量將value寫入buffer。注意:value 必須是一個合法的unsigned 8 bit integer.

設置參數 noAssert爲true表示忽略驗證valueoffset參數。 這意味着 value可能過大,或者offset可能會超出buffer的末尾形成value被丟棄。 這個參數除了你很是有把握,不然不該該使用它。默認是 false。`.

示例:

// <Buffer 03 04 23 42>

buf.writeUInt16LE(value, offset, [noAssert])#

buf.writeUInt16BE(value, offset, [noAssert])#

  • value Number類型
  • offset Number類型
  • noAssert Boolean類型, 可選參數, 默認: false

根據指定的offset偏移量和指定的 endian字節序格式將value寫入buffer。注意:value 必須是一個合法的unsigned 16 bit integer.

設置參數 noAssert爲true表示忽略驗證valueoffset參數。 這意味着 value可能過大,或者offset可能會超出buffer的末尾形成value被丟棄。 這個參數除了你很是有把握,不然不該該使用它。默認是 false。`.

示例:

// <Buffer de ad be ef> // <Buffer ad de ef be>

buf.writeUInt32LE(value, offset, [noAssert])#

buf.writeUInt32BE(value, offset, [noAssert])#

  • value Number類型
  • offset Number類型
  • noAssert Boolean類型, 可選參數, 默認: false

根據指定的offset偏移量和指定的 endian字節序格式將value寫入buffer。注意:value 必須是一個合法的unsigned 32 bit integer。

設置參數 noAssert爲true表示忽略驗證valueoffset參數。 這意味着 value可能過大,或者offset可能會超出buffer的末尾形成value被丟棄。 這個參數除了你很是有把握,不然不該該使用它。默認是 false。`.

示例:

// <Buffer fe ed fa ce> // <Buffer ce fa ed fe>

buf.writeInt8(value, offset, [noAssert])#

  • value Number類型
  • offset Number類型
  • noAssert Boolean類型, 可選參數, 默認: false

根據指定的offset偏移量將value寫入buffer。注意:value 必須是一個合法的 signed 8 bit integer。

設置參數 noAssert爲true表示忽略驗證valueoffset參數。 這意味着 value可能過大,或者offset可能會超出buffer的末尾形成value被丟棄。 這個參數除了你很是有把握,不然不該該使用它。默認是 false。`.

和 buffer.writeUInt8 同樣工做,除非是把有2的補碼的 signed integer 有符號整形寫入buffer

buf.writeInt16LE(value, offset, [noAssert])#

buf.writeInt16BE(value, offset, [noAssert])#

  • value Number類型
  • offset Number類型
  • noAssert Boolean類型, 可選參數, 默認: false

根據指定的offset偏移量和指定的 endian字節序格式將value寫入buffer。注意:value 必須是一個合法的 signed 16 bit integer。

設置參數 noAssert爲true表示忽略驗證valueoffset參數。 這意味着 value可能過大,或者offset可能會超出buffer的末尾形成value被丟棄。 這個參數除了你很是有把握,不然不該該使用它。默認是 false。`.

和 buffer.writeUInt16* 同樣工做,除非是把有2的補碼的 signed integer 有符號整形寫入buffer

buf.writeInt32LE(value, offset, [noAssert])#

buf.writeInt32BE(value, offset, [noAssert])#

  • value Number類型
  • offset Number類型
  • noAssert Boolean類型, 可選參數, 默認: false

根據指定的offset偏移量和指定的 endian字節序格式將value寫入buffer。注意:value 必須是一個合法的 signed 32 bit integer。

設置參數 noAssert爲true表示忽略驗證valueoffset參數。 這意味着 value可能過大,或者offset可能會超出buffer的末尾形成value被丟棄。 這個參數除了你很是有把握,不然不該該使用它。默認是 false。`.

和 buffer.writeUInt32* 同樣工做,除非是把有2的補碼的 signed integer 有符號整形寫入buffer

buf.writeFloatLE(value, offset, [noAssert])#

buf.writeFloatBE(value, offset, [noAssert])#

  • value Number類型
  • offset Number類型
  • noAssert Boolean類型, 可選參數, 默認: false

根據指定的offset偏移量和指定的 endian字節序格式將value寫入buffer。注意:當value 不是一個 32 bit float 類型的值時,結果將是不肯定的。

設置參數 noAssert爲true表示忽略驗證valueoffset參數。 這意味着 value可能過大,或者offset可能會超出buffer的末尾形成value被丟棄。 這個參數除了你很是有把握,不然不該該使用它。默認是 false。`.

示例:

// <Buffer 4f 4a fe bb> // <Buffer bb fe 4a 4f>

buf.writeDoubleLE(value, offset, [noAssert])#

buf.writeDoubleBE(value, offset, [noAssert])#

  • value Number類型
  • offset Number類型
  • noAssert Boolean類型, 可選參數, 默認: false

根據指定的offset偏移量和指定的 endian字節序格式將value寫入buffer。注意:value 必須是一個有效的 64 bit double 類型的值。

設置參數 noAssert爲true表示忽略驗證valueoffset參數。 這意味着 value可能過大,或者offset可能會超出buffer的末尾形成value被丟棄。 這個參數除了你很是有把握,不然不該該使用它。默認是 false。`.

示例:

// <Buffer 43 eb d5 b7 dd f9 5f d7> // <Buffer d7 5f f9 dd b7 d5 eb 43>

buf.fill(value, [offset], [end])#

  • value
  • offset Number類型, 可選參數
  • end Number類型, 可選參數

使用指定的value來填充這個buffer。若是 offset (默認是 0) 而且 end (默認是 buffer.length) 沒有明確給出,就會填充整個buffer。 (譯者:buf.fill調用的是C語言的memset函數很是高效)

var b = new Buffer(50); b.fill("h");

buffer.INSPECT_MAX_BYTES#

  • Number類型, 默認: 50

設置當調用buffer.inspect()方法後,多少bytes將會返回。這個值能夠被用戶模塊重寫。 (譯者:這個值主要用在當咱們打印console.log(buf)時,設置返回多少長度內容)

注意這個屬性是require('buffer')模塊返回的。這個屬性不是在全局變量Buffer中,也再也不buffer的實例裏。 

類: SlowBuffer#

返回一個不被池管理的 Buffer

爲了不建立大量獨立分配的 Buffer 帶來的垃圾回收開銷,默認狀況下小於 4KB 的空間都是切割自一個較大的獨立對象。這種策略既提升了性能也改善了內存使用,由於 V8 不須要跟蹤和清理不少 Persistent 對象。

當開發者須要將池中一小塊數據保留不肯定的一段時間,較爲穩當的辦法是用 SlowBuffer 建立一個不被池管理的 Buffer 實例並將相應數據拷貝出來。

socket.on('readable', function() { var data = socket.read(); // 爲須要保留的數據分配內存 var sb = new SlowBuffer(10); // 將數據拷貝到新的空間中 data.copy(sb, 0, 0, 10); store.push(sb); });

請謹慎使用,僅做爲開發者頻繁觀察到他們的應用中過分的內存保留時的最後手段。

#

穩定度: 2 - 不穩定

流是一個抽象接口,被 Node 中的不少對象所實現。好比對一個 HTTP 服務器的請求是一個流,stdout 也是一個流。流是可讀、可寫或兼具二者的。全部流都是 EventEmitter 的實例。

您能夠經過 require('stream') 加載 Stream 基類,其中包括了 Readable 流、Writable 流、Duplex 流和 Transform 流的基類。

本文檔分爲三個章節。第一章節解釋了您在您的程序中使用流時須要瞭解的那部分 API,若是您不打算本身實現一個流式 API,您能夠只閱讀這一章節。

第二章節解釋了當您本身實現一個流時須要用到的那部分 API,這些 API 是爲了方便您這麼作而設計的。

第三章節深刻講解了流的工做方式,包括一些內部機制和函數,除非您明確知道您在作什麼,不然儘可能不要改動它們。

面向流消費者的 API#

流能夠是可讀(Readable)或可寫(Writable),或者兼具二者(Duplex,雙工)的。

全部流都是 EventEmitter,但它們也具備其它自定義方法和屬性,取決於它們是 Readable、Writable 或 Duplex。

若是一個流既可讀(Readable)也可寫(Writable),則它實現了下文所述的全部方法和事件。所以,這些 API 同時也涵蓋了 Duplex 或 Transform 流,即使它們的實現可能有點不一樣。

爲了消費流而在您的程序中本身實現 Stream 接口是沒有必要的。若是您確實正在您本身的程序中實現流式接口,請同時參考下文面向流實現者的 API

幾乎全部 Node 程序,不管多簡單,都在某種途徑用到了流。這裏有一個使用流的 Node 程序的例子:

var http = require('http'); <!-- endsection --> <!-- section:5dd53fb86ef5aa2fb0a6e831e46cc135 --> var server = http.createServer(function (req, res) { // req 爲 http.IncomingMessage,是一個可讀流(Readable Stream) // res 爲 http.ServerResponse,是一個可寫流(Writable Stream) <!-- endsection --> <!-- section:fd5e086becb475ded97300c6e8b1f889 --> var body = ''; // 咱們打算以 UTF-8 字符串的形式獲取數據 // 若是您不設置編碼,您將獲得一個 Buffer 對象 req.setEncoding('utf8'); <!-- endsection --> <!-- section:bb5a4bf69e5c71de2331fe85918ed96b --> // 一旦監聽器被添加,可讀流會觸發 'data' 事件 req.on('data', function (chunk) { body += chunk; }) <!-- endsection --> <!-- section:5768f3afd395c860ba272f79026a6799 --> // 'end' 事件代表您已經獲得了完整的 body req.on('end', function () { try { var data = JSON.parse(body); } catch (er) { // uh oh! bad json! res.statusCode = 400; return res.end('錯誤: ' + er.message); } <!-- endsection --> <!-- section:812496c72ef4682c63a7ba8837f9610a --> // 向用戶回寫一些有趣的信息 res.write(typeof data); res.end(); }) }) <!-- endsection --> <!-- section:3bbc30d951532659ecc70a505ea1e985 --> server.listen(1337); <!-- endsection --> <!-- section:f0dea661693acf21ed203ec804a4f05a --> // $ curl localhost:1337 -d '{}' // object // $ curl localhost:1337 -d '"foo"' // string // $ curl localhost:1337 -d 'not json' // 錯誤: Unexpected token o

類: stream.Readable#

Readable(可讀)流接口是對您正在讀取的數據的來源的抽象。換言之,數據出自一個 Readable 流。

在您代表您就緒接收以前,Readable 流並不會開始發生數據。

Readable 流有兩種「模式」:流動模式暫停模式。當處於流動模式時,數據由底層系統讀出,並儘量快地提供給您的程序;當處於暫停模式時,您必須明確地調用 stream.read() 來取出若干數據塊。流默認處於暫停模式。

注意:若是沒有綁定 data 事件處理器,而且沒有 pipe() 目標,同時流被切換到流動模式,那麼數據會流失。

您能夠經過下面幾種作法切換到流動模式:

您能夠經過下面其中一種作法切換回暫停模式:

  • 若是沒有導流目標,調用 pause() 方法。
  • 若是有導流目標,移除全部 ['data' 事件][] 處理器、調用 unpipe() 方法移除全部導流目標。

請注意,爲了向後兼容考慮,移除 'data' 事件監聽器並不會自動暫停流。一樣的,當有導流目標時,調用 pause() 並不能保證流在那些目標排空並請求更多數據時維持暫停狀態。

一些可讀流的例子:

事件: 'readable'#

當一個數據塊能夠從流中被讀出時,它會觸發一個 'readable' 事件。

在某些狀況下,假如未準備好,監聽一個 'readable' 事件會使得一些數據從底層系統被讀出到內部緩衝區中。

var readable = getReadableStreamSomehow(); readable.on('readable', function() { // 如今有數據能夠讀了 })

當內部緩衝區被排空後,一旦更多數據時,一個 readable 事件會被再次觸發。

事件: 'data'#

  • chunk {Buffer | String} 數據塊。

綁定一個 data 事件監聽器到一個未被明確暫停的流會將流切換到流動模式,數據會被儘量地傳遞。

若是您想從流盡快取出全部數據,這是最理想的方式。

var readable = getReadableStreamSomehow(); readable.on('data', function(chunk) { console.log('獲得了 %d 字節的數據', chunk.length); })

事件: 'end'#

該事件會在沒有更多數據可以提供時被觸發。

請注意,end 事件在數據被徹底消費以前不會被觸發。這可經過切換到流動模式,或者在到達末端前不斷調用 read() 來實現。

var readable = getReadableStreamSomehow(); readable.on('data', function(chunk) { console.log('獲得了 %d 字節的數據', chunk.length); }) readable.on('end', function() { console.log('讀取完畢。'); });

事件: 'close'#

當底層數據源(好比,源頭的文件描述符)被關閉時觸發。並非全部流都會觸發這個事件。

事件: 'error'#

當數據接收時發生錯誤時觸發。

readable.read([size])#

  • size {Number} 可選參數,指定要讀取多少數據。
  • 返回 {String | Buffer | null}

read() 方法從內部緩衝區中拉取並返回若干數據。當沒有更多數據可用時,它會返回 null

若您傳入了一個 size 參數,那麼它會返回至關字節的數據;當 size 字節不可用時,它則返回 null

若您沒有指定 size 參數,那麼它會返回內部緩衝區中的全部數據。

該方法僅應在暫停模式時被調用。在流動模式中,該方法會被自動調用直到內部緩衝區排空。

var readable = getReadableStreamSomehow(); readable.on('readable', function() { var chunk; while (null !== (chunk = readable.read())) { console.log('獲得了 %d 字節的數據', chunk.length); } });

當該方法返回了一個數據塊,它同時也會觸發 'data' 事件

readable.setEncoding(encoding)#

  • encoding {String} 要使用的編碼。
  • 返回: this

調用此函數會使得流返回指定編碼的字符串而不是 Buffer 對象。好比,當您 readable.setEncoding('utf8'),那麼輸出數據會被做爲 UTF-8 數據解析,並以字符串返回。若是您 readable.setEncoding('hex'),那麼數據會被編碼成十六進制字符串格式。

該方法能正確處理多字節字符。假如您不這麼作,僅僅直接取出 Buffer 並對它們調用 buf.toString(encoding),極可能會致使字節錯位。所以若是您打算以字符串讀取數據,請老是使用這個方法。

var readable = getReadableStreamSomehow(); readable.setEncoding('utf8'); readable.on('data', function(chunk) { assert.equal(typeof chunk, 'string'); console.log('獲得了 %d 個字符的字符串數據', chunk.length); })

readable.resume()#

  • 返回: this

該方法讓可讀流繼續觸發 data 事件。

該方法會將流切換到流動模式。若是您不想從流中消費數據,但您獲得它的 end 事件,您能夠調用 readable.resume() 來啓動數據流。

var readable = getReadableStreamSomehow(); readable.resume(); readable.on('end', function(chunk) { console.log('到達末端,但並未讀取任何東西'); })

readable.pause()#

  • 返回: this

該方法會使一個處於流動模式的流中止觸發 data 事件,切換到非流動模式,並讓後續可用數據留在內部緩衝區中。

var readable = getReadableStreamSomehow(); readable.on('data', function(chunk) { console.log('取得 %d 字節數據', chunk.length); readable.pause(); console.log('接下來 1 秒內不會有數據'); setTimeout(function() { console.log('如今數據會再次開始流動'); readable.resume(); }, 1000); })

readable.pipe(destination, [options])#

  • destination {Writable Stream} 寫入數據的目標
  • options {Object} 導流選項
    • end {Boolean} 在讀取者結束時結束寫入者。缺省爲 true

該方法從可讀流中拉取全部數據,並寫入到所提供的目標。該方法能自動控制流量以免目標被快速讀取的可讀流所淹沒。

能夠導流到多個目標。

var readable = getReadableStreamSomehow(); var writable = fs.createWriteStream('file.txt'); // 全部來自 readable 的數據會被寫入到 'file.txt' readable.pipe(writable);

該函數返回目標流,所以您能夠創建導流鏈:

var r = fs.createReadStream('file.txt'); var z = zlib.createGzip(); var w = fs.createWriteStream('file.txt.gz'); r.pipe(z).pipe(w);

例如,模擬 Unix 的 cat 命令:

process.stdin.pipe(process.stdout);

缺省狀況下當來源流觸發 end 時目標的 end() 會被調用,因此此時 destination 再也不可寫。傳入 { end: false } 做爲 options 可讓目標流保持開啓狀態。

這將讓 writer 保持開啓,所以最後能夠寫入 "Goodbye"。

reader.pipe(writer, { end: false }); reader.on('end', function() { writer.end('Goodbye\n'); });

請注意 process.stderr 和 process.stdout 在進程結束前都不會被關閉,不管是否指定選項。

readable.unpipe([destination])#

  • destination {Writable Stream} 可選,指定解除導流的流

該方法會移除以前調用 pipe() 所設定的鉤子。

若是不指定目標,全部導流都會被移除。

若是指定了目標,但並無與之創建導流,則什麼事都不會發生。

var readable = getReadableStreamSomehow(); var writable = fs.createWriteStream('file.txt'); // 來自 readable 的全部數據都會被寫入 'file.txt', // 但僅發生在第 1 秒 readable.pipe(writable); setTimeout(function() { console.log('中止寫入到 file.txt'); readable.unpipe(writable); console.log('自行關閉文件流'); writable.end(); }, 1000);

readable.unshift(chunk)#

  • chunk {Buffer | String} 要插回讀取隊列開頭的數據塊

該方法在許多場景中都頗有用,好比一個流正在被一個解析器消費,解析器可能須要將某些剛拉取出的數據「逆消費」回來源,以便流能將它傳遞給其它消費者。

若是您發現您須要在您的程序中頻繁調用 stream.unshift(chunk),請考慮實現一個 Transform 流。(詳見下文面向流實現者的 API。)

// 取出以 \n\n 分割的頭部並將多餘部分 unshift() 回去 // callback 以 (error, header, stream) 形式調用 var StringDecoder = require('string_decoder').StringDecoder; function parseHeader(stream, callback) { stream.on('error', callback); stream.on('readable', onReadable); var decoder = new StringDecoder('utf8'); var header = ''; function onReadable() { var chunk; while (null !== (chunk = stream.read())) { var str = decoder.write(chunk); if (str.match(/\n\n/)) { // 找到頭部邊界 var split = str.split(/\n\n/); header += split.shift(); var remaining = split.join('\n\n'); var buf = new Buffer(remaining, 'utf8'); if (buf.length) stream.unshift(buf); stream.removeListener('error', callback); stream.removeListener('readable', onReadable); // 如今能夠從流中讀取消息的主體了 callback(null, header, stream); } else { // 仍在讀取頭部 header += str; } } } }

readable.wrap(stream)#

  • stream {Stream} 一個「舊式」可讀流

Node v0.10 版本以前的流並未實現現今全部流 API。(更多信息詳見下文「兼容性」章節。)

若是您正在使用早前版本的 Node 庫,它觸發 'data' 事件而且有一個僅做查詢用途的 pause() 方法,那麼您能夠使用 wrap() 方法來建立一個使用舊式流做爲數據源的 Readable 流。

您可能不多須要用到這個函數,但它會做爲與舊 Node 程序和庫交互的簡便方法存在。

例如:

myReader.on('readable', function() { myReader.read(); // etc. });

類: stream.Writable#

Writable(可寫)流接口是對您正在寫入數據至一個目標的抽象。

一些可寫流的例子:

writable.write(chunk, [encoding], [callback])#

  • chunk {String | Buffer} 要寫入的數據
  • encoding {String} 編碼,假如 chunk 是一個字符串
  • callback {Function} 數據塊寫入後的回調
  • 返回: {Boolean} 若是數據已被所有處理則 true

該方法向底層系統寫入數據,並在數據被處理完畢後調用所給的回調。

返回值代表您是否應該當即繼續寫入。若是數據須要滯留在內部,則它會返回 false;不然,返回 true

返回值所表示的狀態僅供參考,您【能夠】在即使返回 false 的時候繼續寫入。可是,寫入的數據會被滯留在內存中,因此最好不要過度地這麼作。最好的作法是等待 drain 事件發生後再繼續寫入更多數據。

事件: 'drain'#

若是一個 writable.write(chunk) 調用返回 false,那麼 drain 事件則代表能夠繼續向流寫入更多數據。

// 向所給可寫流寫入 1000000 次數據。 // 注意後端壓力。 function writeOneMillionTimes(writer, data, encoding, callback) { var i = 1000000; write(); function write() { var ok = true; do { i -= 1; if (i === 0) { // 最後一次! writer.write(data, encoding, callback); } else { // 檢查咱們應該繼續仍是等待 // 不要傳遞迴調,由於咱們還沒完成。 ok = writer.write(data, encoding); } } while (i > 0 && ok); if (i > 0) { // 不得不提早中止! // 一旦它排空,繼續寫入數據 writer.once('drain', write); } } }

writable.cork()#

強行滯留全部寫入。

滯留的數據會在 .uncork() 或 .end() 調用時被寫入。

writable.uncork()#

寫入全部 .cork() 調用以後滯留的數據。

writable.end([chunk], [encoding], [callback])#

  • chunk {String | Buffer} 可選,要寫入的數據
  • encoding {String} 編碼,假如 chunk 是一個字符串
  • callback {Function} 可選,流結束後的回調

當沒有更多數據會被寫入到流時調用此方法。若是給出,回調會被用做 finish 事件的監聽器。

在調用 end() 後調用 write() 會產生錯誤。

// 寫入 'hello, ' 而後以 'world!' 結束 http.createServer(function (req, res) { res.write('hello, '); res.end('world!'); // 如今不容許繼續寫入了 });

事件: 'finish'#

當 end() 方法被調用,而且全部數據已被寫入到底層系統,此事件會被觸發。

var writer = getWritableStreamSomehow(); for (var i = 0; i < 100; i ++) { writer.write('hello, #' + i + '!\n'); } writer.end('this is the end\n'); write.on('finish', function() { console.error('已完成全部寫入。'); });

事件: 'pipe'#

  • src {Readable Stream} 導流到本可寫流的來源流

該事件發生於可讀流的 pipe() 方法被調用並添加本可寫流做爲它的目標時。

var writer = getWritableStreamSomehow(); var reader = getReadableStreamSomehow(); writer.on('pipe', function(src) { console.error('某些東西正被導流到 writer'); assert.equal(src, reader); }); reader.pipe(writer);

事件: 'unpipe'#

該事件發生於可讀流的 unpipe() 方法被調用並將本可寫流從它的目標移除時。

var writer = getWritableStreamSomehow(); var reader = getReadableStreamSomehow(); writer.on('unpipe', function(src) { console.error('某寫東西中止導流到 writer 了'); assert.equal(src, reader); }); reader.pipe(writer); reader.unpipe(writer);

類: stream.Duplex#

雙工(Duplex)流同時實現了 Readable 和 Writable 的接口。詳見下文用例。

一些雙工流的例子:

類: stream.Transform#

轉換(Transform)流是一種輸出由輸入計算所得的雙工流。它們同時實現了 Readable 和 Writable 的接口。詳見下文用例。

一些轉換流的例子:

面向流實現者的 API#

不管實現任何形式的流,模式都是同樣的:

  1. 在您的子類中擴充適合的父類。(util.inherits 方法對此頗有幫助。)
  2. 在您的構造函數中調用父類的構造函數,以確保內部的機制被正確初始化。
  3. 實現一個或多個特定的方法,參見下面的細節。

所擴充的類和要實現的方法取決於您要編寫的流類的形式:

使用情景

要實現的方法

只讀

Readable

_read

只寫

Writable

_write

讀寫

Duplex

_read_write

操做被寫入數據,而後讀出結果

Transform

_transform_flush

在您的實現代碼中,十分重要的一點是絕對不要調用上文面向流消費者的 API 中所描述的方法,不然可能在消費您的流接口的程序中產生潛在的反作用。

類: stream.Readable#

stream.Readable 是一個可被擴充的、實現了底層方法 _read(size) 的抽象類。

請閱讀前文面向流消費者的 API 章節瞭解如何在您的程序中消費流。文將解釋如何在您的程序中本身實現 Readable 流。

例子: 一個計數流#

這是一個 Readable 流的基本例子。它將從 1 至 1,000,000 遞增地觸發數字,而後結束。

var Readable = require('stream').Readable; var util = require('util'); util.inherits(Counter, Readable); <!-- endsection --> <!-- section:82b9ddf426e8c00c9a49e4152bdc17fa --> function Counter(opt) { Readable.call(this, opt); this._max = 1000000; this._index = 1; } <!-- endsection --> <!-- section:e0793f568ad1ff897e49e65b3ddff560 --> Counter.prototype._read = function() { var i = this._index++; if (i > this._max) this.push(null); else { var str = '' + i; var buf = new Buffer(str, 'ascii'); this.push(buf); } };

例子: SimpleProtocol v1 (Sub-optimal)#

這個有點相似上文提到的 parseHeader 函數,但它被實現成一個自定義流。一樣地,請注意這個實現並未將傳入數據轉換成字符串。

實際上,更好的辦法是將它實現成一個 Transform 流。更好的實現詳見下文。

// 簡易數據協議的解析器。 // 「header」是一個 JSON 對象,後面緊跟 2 個 \n 字符,以及 // 消息主體。 // // 注意: 使用 Transform 流能更簡單地實現這個功能! // 直接使用 Readable 並非最佳方式,詳見 Transform // 章節下的備選例子。 <!-- endsection --> <!-- section:92b91fe4ba0943c599f1f6f05063281e --> var Readable = require('stream').Readable; var util = require('util'); <!-- endsection --> <!-- section:e1dc23787e59139adcb6395217f4e3e5 --> util.inherits(SimpleProtocol, Readable); <!-- endsection --> <!-- section:4d29aabd4a753ef32e5c07b5a795e855 --> function SimpleProtocol(source, options) { if (!(this instanceof SimpleProtocol)) return new SimpleProtocol(options); <!-- endsection --> <!-- section:71fff3ee938970a8129dae873d7bafb9 --> Readable.call(this, options); this._inBody = false; this._sawFirstCr = false; <!-- endsection --> <!-- section:799ee1f184ce83a81a18b06859ce3631 --> // source 是一個可讀流,好比嵌套字或文件 this._source = source; <!-- endsection --> <!-- section:82425d2c242c810d12229bc70dce5926 --> var self = this; source.on('end', function() { self.push(null); }); <!-- endsection --> <!-- section:2a58126aa0311fb2147d855905f037f8 --> // 當 source 可讀時作點什麼 // read(0) 不會消費任何字節 source.on('readable', function() { self.read(0); }); <!-- endsection --> <!-- section:97e4325ee1de1bff19f7360c6127de91 --> this._rawHeader = []; this.header = null; } <!-- endsection --> <!-- section:d944bcef0e5bd7b58955e7c2e7640ca3 --> SimpleProtocol.prototype._read = function(n) { if (!this._inBody) { var chunk = this._source.read(); <!-- endsection --> <!-- section:7dd79fb9f97bbd18362b6ed55be8bb79 --> if (split === -1) { // 繼續等待 \n\n // 暫存數據塊,並再次嘗試 this._rawHeader.push(chunk); this.push(''); } else { this._inBody = true; var h = chunk.slice(0, split); this._rawHeader.push(h); var header = Buffer.concat(this._rawHeader).toString(); try { this.header = JSON.parse(header); } catch (er) { this.emit('error', new Error('invalid simple protocol data')); return; } // 如今,咱們獲得了一些多餘的數據,因此須要 unshift // 將多餘的數據放回讀取隊列以便咱們的消費者可以讀取 var b = chunk.slice(split); this.unshift(b); <!-- endsection --> <!-- section:9cc80d286b7ec752e3ae5fb819e63392 --> // 並讓它們知道咱們完成了頭部解析。 this.emit('header', this.header); } } else { // 從如今開始,僅需向咱們的消費者提供數據。 // 注意不要 push(null),由於它代表 EOF。 var chunk = this._source.read(); if (chunk) this.push(chunk); } }; <!-- endsection --> <!-- section:ab30c3ee01c1cd6af24cd93ee043216f --> // 用法: // var parser = new SimpleProtocol(source); // 如今 parser 是一個會觸發 'header' 事件並提供已解析 // 的頭部的可讀流。

new stream.Readable([options])#

  • options {Object}
    • highWaterMark {Number} 中止從底層資源讀取前內部緩衝區最多能存放的字節數。缺省爲 16kb,對於 objectMode 流則是 16
    • encoding {String} 若給出,則 Buffer 會被解碼成所給編碼的字符串。缺省爲 null
    • objectMode {Boolean} 該流是否應該表現爲對象的流。意思是說 stream.read(n) 返回一個單獨的對象,而不是大小爲 n 的 Buffer

請確保在擴充 Readable 類的類中調用 Readable 構造函數以便緩衝設定能被正確初始化。

readable._read(size)#

  • size {Number} 異步讀取的字節數

注意:實現這個函數,但【不要】直接調用它。

這個函數【不該該】被直接調用。它應該被子類所實現,並僅被 Readable 類內部方法所調用。

全部 Readable 流的實現都必須提供一個 _read 方法來從底層資源抓取數據。

該方法如下劃線開頭是由於它對於定義它的類是內部的,而且不該該被用戶程序直接調用。可是,你應當在您的擴充類中覆蓋這個方法。

當數據可用時,調用 readable.push(chunk) 將它加入到讀取隊列。若是 push 返回 false,那麼您應該中止讀取。當 _read 被再次調用,您應該繼續推出更多數據。

參數 size 僅做查詢。「read」調用返回數據的實現能夠經過這個參數來知道應當抓取多少數據;其他與之無關的實現,好比 TCP 或 TLS,則可忽略這個參數,並在可用時返回數據。例如,沒有必要「等到」 size 個字節可用時才調用 stream.push(chunk)

readable.push(chunk, [encoding])#

  • chunk {Buffer | null | String} 推入讀取隊列的數據塊
  • encoding {String} 字符串塊的編碼。必須是有效的 Buffer 編碼,好比 utf8 或 ascii
  • 返回 {Boolean} 是否應該繼續推入

注意:這個函數應該被 Readable 實現者調用,【而不是】Readable 流的消費者。

函數 _read() 不會被再次調用,直到至少調用了一次 push(chunk)

Readable 類的工做方式是,將數據讀入一個隊列,當 'readable' 事件發生、調用 read() 方法時,數據會被從隊列中取出。

push() 方法會明確地向讀取隊列中插入一些數據。若是調用它時傳入了 null 參數,那麼它會觸發數據結束信號(EOF)。

這個 API 被設計成儘量地靈活。好比說,您能夠包裝一個低級別的具有某種暫停/恢復機制和數據回調的數據源。這種狀況下,您能夠經過這種方式包裝低級別來源對象:

// source 是一個帶 readStop() 和 readStart() 方法的類, // 以及一個當有數據時會被調用的 `ondata` 成員、一個 // 當數據結束時會被調用的 `onend` 成員。 <!-- endsection --> <!-- section:95e3ecd4260c781a6024a021bc68e57e --> util.inherits(SourceWrapper, Readable); <!-- endsection --> <!-- section:6007ea5475e96279c2e93631f4336467 --> function SourceWrapper(options) { Readable.call(this, options); <!-- endsection --> <!-- section:da7e608bbd3803cd4c5f822ebe9be93c --> this._source = getLowlevelSourceObject(); var self = this; <!-- endsection --> <!-- section:2cf3dadadeb5f48299a1121bf6a40a8b --> // 每當有數據時,咱們將它推入到內部緩衝區中 this._source.ondata = function(chunk) { // 若是 push() 返回 false,咱們就須要暫停讀取 source if (!self.push(chunk)) self._source.readStop(); }; <!-- endsection --> <!-- section:4d599b75f53a964c3f5d0db3a9ad12b0 --> // 當來源結束時,咱們 push 一個 `null` 塊以表示 EOF this._source.onend = function() { self.push(null); }; } <!-- endsection --> <!-- section:dab6a1aaf2a7fa07f84f58bfbd3f8a61 --> // _read 會在流想要拉取更多數據時被調用 // 本例中忽略 size 參數 SourceWrapper.prototype._read = function(size) { this._source.readStart(); };

類: stream.Writable#

stream.Writable 是一個可被擴充的、實現了底層方法 _write(chunk, encoding, callback) 的抽象類。

請閱讀前文面向流消費者的 API 章節瞭解如何在您的程序中消費可讀流。下文將解釋如何在您的程序中本身實現 Writable 流。

new stream.Writable([options])#

  • options {Object}
    • highWaterMark {Number} write() 開始返回 false 的緩衝級別。缺省爲 16kb,對於 objectMode 流則是 16
    • decodeStrings {Boolean} 是否在傳遞給 _write() 前將字符串解碼成 Buffer。缺省爲 true

請確保在擴充 Writable 類的類中調用構造函數以便緩衝設定能被正確初始化。

writable._write(chunk, encoding, callback)#

  • chunk {Buffer | String} 要被寫入的數據塊。總會是一個 Buffer,除非 decodeStrings 選項被設定爲 false
  • encoding {String} 若是數據塊是字符串,則這裏指定它的編碼類型。若是數據塊是 Buffer 則忽略此設定。請注意數據塊總會是一個 Buffer,除非 decodeStrings 選項被明確設定爲 false
  • callback {Function} 當您處理完所給數據塊時調用此函數(可選地可附上一個錯誤參數)。

全部 Writable 流的實現必須提供一個 _write() 方法來將數據發送到底層資源。

注意:該函數【禁止】被直接調用。它應該被子類所實現,並僅被 Writable 內部方法所調用。

使用標準的 callback(error) 形式來調用回調以代表寫入成功完成或遇到錯誤。

若是構造函數選項中設定了 decodeStrings 標誌,則 chunk 可能會是字符串而不是 Buffer,而且 encoding代表了字符串的格式。這種設計是爲了支持對某些字符串數據編碼提供優化處理的實現。若是您沒有明確地將 decodeStrings 選項設定爲 false,那麼您能夠安全地忽略 encoding 參數,並假定 chunk 老是一個 Buffer。

該方法如下劃線開頭是由於它對於定義它的類是內部的,而且不該該被用戶程序直接調用。可是,你應當在您的擴充類中覆蓋這個方法。

writable._writev(chunks, callback)#

  • chunks {Array} 要寫入的塊。每一個塊都遵循這種格式:{ chunk: ..., encoding: ... }
  • callback {Function} 當您處理完所給數據塊時調用此函數(可選地可附上一個錯誤參數)。

注意:該函數【禁止】被直接調用。它應該被子類所實現,並僅被 Writable 內部方法所調用。

該函數的實現徹底是可選的,在大多數狀況下都是沒必要要的。若是實現,它會被以全部滯留在寫入隊列中的數據塊調用。

類: stream.Duplex#

「雙工」(duplex)流同時兼具可讀和可寫特性,好比一個 TCP 嵌套字鏈接。

值得注意的是,stream.Duplex 是一個能夠像 Readable 或 Writable 同樣被擴充、實現了底層方法 _read(sise) 和 _write(chunk, encoding, callback) 的抽象類。

因爲 JavaScript 並不具有多原型繼承能力,這個類實際上繼承自 Readable,並寄生自 Writable,從而讓用戶在雙工類的擴充中能同時實現低級別的 _read(n) 方法和 _write(chunk, encoding, callback) 方法。

new stream.Duplex(options)#

  • options {Object} Passed to both Writable and Readable constructors. Also has the following fields:
    • allowHalfOpen {Boolean} Default=true. If set to false, then the stream will automatically end the readable side when the writable side ends and vice versa.

請確保在擴充 Duplex 類的類中調用構造函數以便緩衝設定能被正確初始化。

類: stream.Transform#

「轉換」(transform)流其實是一個輸出與輸入存在因果關係的雙工流,好比 zlib 流或 crypto 流。

輸入和輸出並沒有要求相同大小、相同塊數或同時到達。舉個例子,一個 Hash 流只會在輸入結束時產生一個數據塊的輸出;一個 zlib 流會產生比輸入小得多或大得多的輸出。

轉換類必須實現 _transform() 方法,而不是 _read() 和 _write() 方法。可選的,也能夠實現 _flush()方法。(詳見下文。)

new stream.Transform([options])#

  • options {Object} 傳遞給 Writable 和 Readable 構造函數。

請確保在擴充 Transform 類的類中調用了構造函數,以使得緩衝設定能被正確初始化。

transform._transform(chunk, encoding, callback)#

  • chunk {Buffer | String} 要被轉換的數據塊。老是 Buffer,除非 decodeStrings 選項被設定爲 false
  • encoding {String} 若是數據塊是一個字符串,那麼這就是它的編碼類型。(數據塊是 Buffer 則會忽略此參數。)
  • callback {Function} 當您處理完所提供的數據塊時調用此函數(可選地附上一個錯誤參數)。

注意:該函數【禁止】被直接調用。它應該被子類所實現,並僅被 Transform 內部方法所調用。

全部轉換流的實現都必須提供一個 _transform 方法來接受輸入併產生輸出。

_transform 應當承擔特定 Transform 類中全部處理被寫入的字節、並將它們丟給接口的可寫端的職責,進行異步 I/O,處理其它事情等等。

調用 transform.push(outputChunk) 0 或屢次來從輸入塊生成輸出,取決於您想從這個數據塊輸出多少數據。

僅噹噹前數據塊被徹底消費時調用回調函數。注意,任何特定的輸入塊都有可能或可能不會產生輸出。

該方法如下劃線開頭是由於它對於定義它的類是內部的,而且不該該被用戶程序直接調用。可是,你應當在您的擴充類中覆蓋這個方法。

transform._flush(callback)#

  • callback {Function} 當您寫入完畢剩下的數據後調用此函數(可選地可附上一個錯誤對象)。

注意:該函數【禁止】被直接調用。它【能夠】被子類所實現,而且若是實現,僅被 Transform 內部方法所調用。

在一些情景中,您的轉換操做可能須要在流的末尾多發生一點點數據。例如,一個 Zlib 壓縮流會儲存一些內部狀態以便更好地壓縮輸出,但在最後它須要儘量好地處理剩下的東西以使數據完整。

在這種狀況中,您能夠實現一個 _flush 方法,它會在最後被調用,在全部寫入數據被消費、但在觸發 end 表示可讀端到達末尾以前。和 _transform 同樣,只需在寫入操做完成時適當地調用 transform.push(chunk)零或屢次。

該方法如下劃線開頭是由於它對於定義它的類是內部的,而且不該該被用戶程序直接調用。可是,你應當在您的擴充類中覆蓋這個方法。

例子: SimpleProtocol 解析器 v2#

上文的簡易協議解析器例子可以很簡單地使用高級別 Transform 流類實現,相似於前文 parseHeader 和 SimpleProtocal v1 示例。

在這個示例中,輸入會被導流到解析器中,而不是做爲參數提供。這種作法更符合 Node 流的慣例。

var util = require('util'); var Transform = require('stream').Transform; util.inherits(SimpleProtocol, Transform); <!-- endsection --> <!-- section:22418c0818055544bb6f8097f23bfeff --> function SimpleProtocol(options) { if (!(this instanceof SimpleProtocol)) return new SimpleProtocol(options); <!-- endsection --> <!-- section:d3b6e2613e286e415707007520ea9c3b --> Transform.call(this, options); this._inBody = false; this._sawFirstCr = false; this._rawHeader = []; this.header = null; } <!-- endsection --> <!-- section:e0759f379211c5242519301000ad97b4 --> SimpleProtocol.prototype._transform = function(chunk, encoding, done) { if (!this._inBody) { // 檢查數據塊是否有 \n\n var split = -1; for (var i = 0; i < chunk.length; i++) { if (chunk[i] === 10) { // '\n' if (this._sawFirstCr) { split = i; break; } else { this._sawFirstCr = true; } } else { this._sawFirstCr = false; } } <!-- endsection --> <!-- section:e97904f9981d2c5c074f860e23f24a1a --> if (split === -1) { // 仍舊等待 \n\n // 暫存數據塊並重試。 this._rawHeader.push(chunk); } else { this._inBody = true; var h = chunk.slice(0, split); this._rawHeader.push(h); var header = Buffer.concat(this._rawHeader).toString(); try { this.header = JSON.parse(header); } catch (er) { this.emit('error', new Error('invalid simple protocol data')); return; } // 並讓它們知道咱們完成了頭部解析。 this.emit('header', this.header); <!-- endsection --> <!-- section:31bb3371c6edccf470ad2539a443e5a3 --> // 如今,因爲咱們得到了一些額外的數據,先觸發這個。 this.push(chunk.slice(split)); } } else { // 以後,僅需向咱們的消費者原樣提供數據。 this.push(chunk); } done(); }; <!-- endsection --> <!-- section:49fb3d6151e897882f69ee67a4f301b4 --> // 用法: // var parser = new SimpleProtocol(); // source.pipe(parser) // 如今 parser 是一個會觸發 'header' 並帶上解析後的 // 頭部數據的可讀流。

類: stream.PassThrough#

這是 Transform 流的一個簡單實現,將輸入的字節簡單地傳遞給輸出。它的主要用途是演示和測試,但偶爾要構建某種特殊流的時候也能派上用場。

流:內部細節#

緩衝#

不管 Writable 或 Readable 流都會在內部分別叫作 _writableState.buffer 和 _readableState.buffer 的對象中緩衝數據。

被緩衝的數據量取決於傳遞給構造函數的 highWaterMark(最高水位線)選項。

Readable 流的滯留髮生於當實現調用 stream.push(chunk) 的時候。若是流的消費者沒有調用 stream.read(),那麼數據將會一直待在內部隊列,直到它被消費。

Writable 流的滯留髮生於當用戶重複調用 stream.write(chunk) 即使此時 write() 返回 false 時。

流,尤爲是 pipe() 方法的初衷,是將數據的滯留量限制到一個可接受的水平,以使得不一樣速度的來源和目標不會淹沒可用內存。

stream.read(0)#

在某寫情景中,您可能須要觸發底層可讀流機制的刷新,但不真正消費任何數據。在這中狀況下,您能夠調用 stream.read(0),它總會返回 null

若是內部讀取緩衝低於 highWaterMark 水位線,而且流當前不在讀取狀態,那麼調用 read(0) 會觸發一個低級 _read 調用。

雖然幾乎沒有必要這麼作,但您能夠在 Node 內部的某些地方看到它確實這麼作了,尤爲是在 Readable 流類的內部。

stream.push('')#

推入一個零字節字符串或 Buffer(當不在 對象模式 時)有一個有趣的反作用。由於它是一個對 stream.push()的調用,它會結束 reading 進程。然而,它沒有添加任何數據到可讀緩衝中,因此沒有東西能夠被用戶消費。

在極少數狀況下,您當時沒有數據提供,但您的流的消費者(或您的代碼的其它部分)會經過調用 stream.read(0) 得知什麼時候再次檢查。在這中狀況下,您能夠調用 stream.push('')

到目前爲止,這個功能惟一一個使用情景是在 tls.CryptoStream 類中,但它將在 Node v0.12 中被廢棄。若是您發現您不得不使用 stream.push(''),請考慮另外一種方式,由於幾乎能夠明確代表這是某種可怕的錯誤。

與 Node 早期版本的兼容性#

在 v0.10 以前版本的 Node 中,Readable 流的接口較爲簡單,同時功能和實用性也較弱。

  • 'data' 事件會開始當即開始發生,而不會等待您調用 read() 方法。若是您須要進行某些 I/O 來決定如何處理數據,那麼您只能將數據塊儲存到某種緩衝區中以防它們流失。
  • pause() 方法僅起提議做用,而不保證生效。這意味着,即使當流處於暫停狀態時,您仍然須要準備接收 'data' 事件。

在 Node v0.10 中,下文所述的 Readable 類被加入進來。爲了向後兼容考慮,Readable 流會在添加了 'data' 事件監聽器、或 resume() 方法被調用時切換至「流動模式」。其做用是,即使您不使用新的 read() 方法和 'readable' 事件,您也沒必要擔憂丟失 'data' 數據塊。

大多數程序會維持正常功能,然而,這也會在下列條件下引入一種邊界狀況:

  • 沒有添加 'data' 事件處理器。
  • resume() 方法從未被調用。
  • 流未被導流到任何可寫目標。

舉個例子,請留意下面代碼:

// 警告!不能用! net.createServer(function(socket) { <!-- endsection --> <!-- section:08da922ddfb188b15f60f9d8d2751a66 --> // 咱們添加了一個 'end' 事件,但從未消費數據 socket.on('end', function() { // 它永遠不會到達這裏 socket.end('我收到了您的來信(但我沒看它)\n'); }); <!-- endsection --> <!-- section:15718ac0ffde3852abd2837cb5ffce33 --> }).listen(1337);

在 Node v0.10 以前的版本中,傳入消息數據會被簡單地丟棄。然而在 Node v0.10 及以後,socket 會一直保持暫停。

對於這種情形的妥協方式是調用 resume() 方法來開啓數據流:

// 妥協 net.createServer(function(socket) { <!-- endsection --> <!-- section:818557d4b3cecb62fa0a224bac43a894 --> socket.on('end', function() { socket.end('我收到了您的來信(但我沒看它)\n'); }); <!-- endsection --> <!-- section:d3a1f09536e7ab650311327cd3264147 --> // 開啓數據流,並丟棄它們。 socket.resume(); <!-- endsection --> <!-- section:15718ac0ffde3852abd2837cb5ffce33 --> }).listen(1337);

額外的,對於切換到流動模式的新 Readable 流,v0.10 以前風格的流能夠經過 wrap() 方法被包裝成 Readable 類。

對象模式#

一般狀況下,流只操做字符串和 Buffer。

處於對象模式的流除了 Buffer 和字符串外還能讀出普通的 JavaScript 值。

一個處於對象模式的 Readable 流調用 stream.read(size) 時總會返回單個項目,不管傳入什麼 size 參數。

一個處於對象模式的 Writable 流老是會忽略傳給 stream.write(data, encoding) 的 encoding 參數。

特殊值 null 在對象模式流中依舊保持它的特殊性。也就說,對於對象模式的可讀流,stream.read() 返回 null 意味着沒有更多數據,同時 stream.push(null) 會告知流數據到達末端(EOF)。

Node 核心不存在對象模式的流,這種設計只被某些用戶態流式庫所使用。

您應該在您的流子類構造函數的選項對象中設置 objectMode。在流的過程當中設置 objectMode 是不安全的。

狀態對象#

Readable 流有一個成員對象叫做 _readableState。 Writable 流有一個成員對象叫做 _writableStateDuplex 流兩者兼備。

這些對象一般不該該被子類所更改。然而,若是您有一個 Duplex 或 Transform 流,它的可讀端應該是 objectMode,但可寫端卻又不是 objectMode,那麼您能夠在構造函數裏明確地設定合適的狀態對象的標記來達到此目的。

var util = require('util'); var StringDecoder = require('string_decoder').StringDecoder; var Transform = require('stream').Transform; util.inherits(JSONParseStream, Transform); <!-- endsection --> <!-- section:7123a6445c6afaf75360315f05cd5634 --> // 獲取以 \n 分隔的 JSON 字符串數據,並丟出解析後的對象 function JSONParseStream(options) { if (!(this instanceof JSONParseStream)) return new JSONParseStream(options); <!-- endsection --> <!-- section:1ee7fdeecca2f4145faa196231702628 --> Transform.call(this, options); this._writableState.objectMode = false; this._readableState.objectMode = true; this._buffer = ''; this._decoder = new StringDecoder('utf8'); } <!-- endsection --> <!-- section:e80bef7f89055305490b77af751dbaea --> JSONParseStream.prototype._transform = function(chunk, encoding, cb) { this._buffer += this._decoder.write(chunk); // 以新行分割 var lines = this._buffer.split(/\r?\n/); // 保留最後一行被緩衝 this._buffer = lines.pop(); for (var l = 0; l < lines.length; l++) { var line = lines[l]; try { var obj = JSON.parse(line); } catch (er) { this.emit('error', er); return; } // 推出解析後的對象到可讀消費者 this.push(obj); } cb(); }; <!-- endsection --> <!-- section:5327c37bec579bd884e1991cfe0d5226 --> JSONParseStream.prototype._flush = function(cb) { // 僅僅處理剩下的東西 var rem = this._buffer.trim(); if (rem) { try { var obj = JSON.parse(rem); } catch (er) { this.emit('error', er); return; } // 推出解析後的對象到可讀消費者 this.push(obj); } cb(); };

狀態對象包含了其它調試您的程序的流的狀態時有用的信息。讀取它們是能夠的,但越過構造函數的選項來更改它們是不安全的

加密(Crypto)#

穩定度: 2 - 不穩定;正在討論將來版本的API變更。會盡可能減小重大變更的發生。詳見下文。

使用 require('crypto') 來調用該模塊。

crypto模塊提供在HTTPS或HTTP鏈接中封裝安全憑證的方法.

它提供OpenSSL中的一系列哈希方法,包括hmac、cipher、decipher、簽名和驗證等方法的封裝。

crypto.getCiphers()#

返回一個數組,包含支持的加密算法的名字。

示例:

var ciphers = crypto.getCiphers(); console.log(ciphers); // ['AES-128-CBC', 'AES-128-CBC-HMAC-SHA1', ...]

crypto.getHashes()#

返回一個包含所支持的哈希算法的數組。

示例:

var hashes = crypto.getHashes(); console.log(hashes); // ['sha', 'sha1', 'sha1WithRSAEncryption', ...]

crypto.createCredentials(details)#

建立一個加密憑證對象,接受一個可選的參數對象:

  • pfx : 一個字符串或者buffer對象,表明經PFX或者PKCS12編碼產生的私鑰、證書以及CA證書
  • key : 一個字符串,表明經PEM編碼產生的私鑰
  • passphrase : 私鑰或者pfx的密碼
  • cert : 一個字符串,表明經PEM編碼產生的證書
  • ca : 一個字符串或者字符串數組,表示可信任的經PEM編碼產生的CA證書列表
  • crl : 一個字符串或者字符串數組,表示經PEM編碼產生的CRL(證書吊銷列表 Certificate Revocation List)
  • ciphers: 一個字符串,表示須要使用或者排除的加密算法 能夠在http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT 查看更多關於加密算法格式的資料。

若是沒有指定ca,node.js會使用http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt提供的公共可信任的CA列表。

crypto.createHash(algorithm)#

建立並返回一個哈希對象,一個使用所給算法的用於生成摘要的加密哈希。

algorithm 取決與平臺上所安裝的 OpenSSL 版本所支持的算法。好比 'sha1''md5''sha256''sha512' 等等。在最近的發行版本中,openssl list-message-digest-algorithms 會顯示可用的摘要算法。

例子:這段程序會計算出一個文件的 sha1 摘要值。

s.on('end', function() { var d = shasum.digest('hex'); console.log(d + ' ' + filename); });

類: Hash#

建立數據哈希摘要的類。

它是一個既可讀又可寫的。所寫入的數據會被用做計算哈希。當流的可寫端終止後,使用 read() 方法來獲取計算得的哈希摘要。同時也支持舊有的 update 和 digest 方法。

經過 crypto.createHash 返回。

hash.update(data, [input_encoding])#

經過提供的數據更新哈希對象,能夠經過input_encoding指定編碼爲'utf8''ascii'或者 'binary'。若是沒有指定編碼,將做爲二進制數據(buffer)處理。

由於它是流式數據,因此能夠使用不一樣的數據調用不少次。

hash.digest([encoding])#

計算傳入的全部數據的摘要值。encoding能夠是'hex''binary'或者'base64',若是沒有指定,會返回一個buffer對象。

注意:hash 對象在 digest() 方法被調用後將不可用。

crypto.createHmac(algorithm, key)#

建立並返回一個hmac對象,也就是經過給定的加密算法和密鑰生成的加密圖譜(cryptographic)。

它是一個既可讀又可寫的流(stream)。寫入的數據會被用於計算hmac。寫入終止後,能夠使用read()方法獲取計算後的摘要值。以前版本的updatedigest方法仍然支持。

algorithm在OpenSSL支持的算法列表中被拋棄了——見上方createHash部分。key是hmac算法用到的密鑰。

Class: Hmac#

用於建立hmac加密圖譜(cryptographic)的類。

crypto.createHmac返回。

hmac.update(data)#

經過提供的數據更新hmac對象。由於它是流式數據,因此能夠使用新數據調用不少次。

hmac.digest([encoding])#

計算傳入的全部數據的hmac摘要值。encoding能夠是'hex''binary'或者'base64',若是沒有指定,會返回一個buffer對象。

注意: hmac對象在調用digest()以後就再也不可用了。

crypto.createCipher(algorithm, password)#

用給定的算法和密碼,建立並返回一個cipher加密算法的對象。(譯者:cipher 就是加密算法的意思, ssl 的 cipher 主要是對稱加密算法和不對稱加密算法的組合。)

algorithm算法是依賴OpenSSL庫的, 例如: 'aes192'算法等。在最近發佈的版本, 執行命令 openssl list-cipher-algorithms 就會顯示出全部可用的加密算法,password是用來派生key和IV的,它必須是一個 'binary' 2進制格式的字符串或者是一個buffer。(譯者:key表示密鑰,IV表示向量在加密過程和解密過程都要使用)

它是一個既可讀又可寫的。所寫入的數據會被用做計算哈希。當流的可寫端終止後,使用 read() 方法來獲取計算得的哈希摘要。同時也支持舊有的 update 和 digest 方法。

crypto.createCipheriv(algorithm, key, iv)#

用給定的算法、密碼和向量,建立並返回一個cipher加密算法的對象。

algorithm算法和createCipher() 方法的參數相同.  key密鑰是一個被算法使用的原始密鑰,iv是一個初始化向量

key密鑰和iv向量必須是'binary'2進制格式的字符串或buffers.

Class: Cipher#

這個類是用來加密數據的。

這個類由 crypto.createCipher 和 crypto.createCipheriv 返回。

Cipher加密對象是 streams,他是具備 readable 可讀和 writable 可寫的。寫入的純文本數據是用來在可讀流一側加密數據的。 之前版本的update 和final方法也仍是支持的。

cipher.update(data, [input_encoding], [output_encoding])#

data參數更新cipher加密對象, 它的編碼input_encoding必須是下列給定編碼的 'utf8''ascii' or 'binary' 中一種。若是沒有編碼參數,那麼打他參數必須是一個buffer。

參數 output_encoding輸出編碼指定了加密數據的輸出格式,能夠是'binary''base64' 或者'hex',若是沒有提供這個參數,buffer將會返回。

返回加密內容,而且Returns the enciphered contents, 用新數據做爲流的話,它能夠被調用屢次。

cipher.final([output_encoding])#

返回剩餘的加密內容,output_encoding'binary''base64' 或 'hex'中的任意一個。 若是沒有提供編碼格式,則返回一個buffer對象。

注: 調用final()函數後cipher 對象不能被使用。

cipher.setAutoPadding(auto_padding=true)#

對於將輸入數據自動填充到塊大小的功能,你能夠將其禁用。若是auto_padding是false, 那麼整個輸入數據的長度必須是加密器的塊大小的整倍數,不然final會失敗。這對非標準的填充頗有用,例如使用0x0而不是PKCS的填充。這個函數必須在cipher.final以前調用。

crypto.createDecipher(algorithm, password)#

根據給定的算法和密鑰,建立並返回一個解密器對象。這是上述createCipher()的一個鏡像。

crypto.createDecipheriv(algorithm, key, iv)#

Creates and returns a decipher object, with the given algorithm, key and iv. This is the mirror of the createCipheriv() above. 根據給定的算法,密鑰和初始化向量,建立並返回一個解密器對象。這是上述createCipheriv()的一個鏡像。

Class: Decipher#

解密數據的類。

crypto.createDeciphercrypto.createDecipheriv返回。

解密器對象是可讀寫的對象。用被寫入的加密數據生成可讀的平文數據。解碼器對象也支持The legacy update和 final函數。

decipher.update(data, [input_encoding], [output_encoding])#

data來更新解密器,其中data'binary''base64' 或 'hex'進行編碼。若是沒有指明編碼方式,則默認data是一個buffer對象。

output_decoding指明瞭用如下哪一種編碼方式返回解密後的平文:'binary''ascii' 或 'utf8'。若是沒有指明編碼方式,則返回一個buffer對象。

decipher.final([output_encoding])#

返回剩餘的加密內容,output_encoding'binary''ascii' 或 'utf8'中的任意一個。若是沒有指明編碼方式,則返回一個buffer對象。

注: 調用final()函數後不能使用decipher 對象。

decipher.setAutoPadding(auto_padding=true)#

若是數據以非標準的塊填充方式被加密,那麼你能夠禁用自動填充來防止decipher.final對數據進行檢查和移除。這隻有在輸入數據的長度是加密器塊大小的整倍數時纔有效。這個函數必須在將數據流傳遞給decipher.update以前調用。

crypto.createSign(algorithm)#

根據給定的算法,建立並返回一個signing對象。在最近的OpenSSL發佈版本中,openssl list-public-key-algorithms會列出可用的簽名算法,例如'RSA-SHA256'

Class: Sign#

生成數字簽名的類

crypto.createSign返回。

Sign對象是可寫的對象。被寫入的數據用來生成數字簽名。當全部的數據都被寫入後,sign 函數會返回數字簽名。Sign對象也支持The legacy update函數。

sign.update(data)#

data來更新sign對象。 This can be called many times with new data as it is streamed.

sign.sign(private_key, [output_format])#

根據全部傳送給sign的更新數據來計算電子簽名。private_key是一個包含了簽名私鑰的字符串,而該私鑰是用PEM編碼的。

返回一個數字簽名,該簽名的格式能夠是'binary''hex'或 'base64'. 若是沒有指明編碼方式,則返回一個buffer對象。

注:調用sign()後不能使用sign對象。

crypto.createVerify(algorithm)#

根據指明的算法,建立並返回一個驗證器對象。這是上述簽名器對象的鏡像。

Class: Verify#

用來驗證數字簽名的類。

由 crypto.createVerify返回。

驗證器對象是可寫的對象. 被寫入的數據會被用來驗證提供的數字簽名。在全部的數據被寫入後,若是提供的數字簽名有效,verify函數會返回真。驗證器對象也支持 The legacy update函數。

verifier.update(data)#

用數據更新驗證器對象。This can be called many times with new data as it is streamed.

verifier.verify(object, signature, [signature_format])#

objectsignature來驗證被簽名的數據。 object是一個字符串,這個字符串包含了一個被PEM編碼的對象,這個對象能夠是RSA公鑰,DSA公鑰或者X.509 證書。 signature是以前計算出來的數字簽名,其中的 signature_format能夠是'binary''hex' 或 'base64'. 若是沒有指明編碼方式,那麼默認是一個buffer對象。

根據數字簽名對於數據和公鑰的有效性,返回true或false。

注: 調用verify()函數後不能使用verifier對象。

crypto.createDiffieHellman(prime_length)#

建立一個迪菲-赫爾曼密鑰交換(Diffie-Hellman key exchange)對象,並根據給定的位長度生成一個質數。所用的生成器是s

crypto.createDiffieHellman(prime, [encoding])#

根據給定的質數建立一個迪菲-赫爾曼密鑰交換(Diffie-Hellman key exchange)對象。 所用的生成器是2。編碼方式能夠是'binary''hex'或 'base64'。若是沒有指明編碼方式,則默認是一個buffer對象。

Class: DiffieHellman#

建立迪菲-赫爾曼密鑰交換(Diffie-Hellman key exchanges)的類。

crypto.createDiffieHellman返回。

diffieHellman.generateKeys([encoding])#

生成迪菲-赫爾曼(Diffie-Hellman)算法的公鑰和私鑰,並根據指明的編碼方式返回公鑰。這個公鑰能夠轉交給第三方。編碼方式能夠是 'binary''hex'或 'base64'. 若是沒有指明編碼方式,則返回一個buffer對象。

diffieHellman.computeSecret(other_public_key, [input_encoding], [output_encoding])#

other_public_key做爲第三方公鑰來計算共享祕密,並返回這個共享祕密。參數中的密鑰會以input_encoding編碼方式來解讀,而共享密鑰則會用output_encoding進行編碼。編碼方式能夠是'binary''hex'或 'base64'。若是沒有提供輸入的編碼方式,則默認爲一個buffer對象。

若是沒有指明輸出的編碼方式,則返回一個buffer對象。

diffieHellman.getPrime([encoding])#

根據指明的編碼格式返回迪菲-赫爾曼(Diffie-Hellman)質數,其中編碼方式能夠是'binary''hex' 或 'base64'。若是沒有指明編碼方式,則返回一個buffer對象。

diffieHellman.getGenerator([encoding])#

根據指明的編碼格式返回迪菲-赫爾曼(Diffie-Hellman)質數,其中編碼方式能夠是'binary''hex' 或 'base64'。若是沒有指明編碼方式,則返回一個buffer對象。

diffieHellman.getPublicKey([encoding])#

根據指明的編碼格式返回迪菲-赫爾曼(Diffie-Hellman)公鑰,其中編碼方式能夠是'binary''hex' 或 'base64'。 若是沒有指明編碼方式,則返回一個buffer對象。

diffieHellman.getPrivateKey([encoding])#

根據指明的編碼格式返回迪菲-赫爾曼(Diffie-Hellman)私鑰,其中編碼方式能夠是'binary''hex' 或 'base64'。若是沒有指明編碼方式,則返回一個buffer對象。

diffieHellman.setPublicKey(public_key, [encoding])#

設置迪菲-赫爾曼(Diffie-Hellman)公鑰,編碼方式能夠是能夠是'binary''hex' 或 'base64'。若是沒有指明編碼方式,則返回一個buffer對象。

diffieHellman.setPrivateKey(private_key, [encoding])#

設置迪菲-赫爾曼(Diffie-Hellman)私鑰,編碼方式能夠是能夠是'binary''hex' 或 'base64'。若是沒有指明編碼方式,則返回一個buffer對象。

crypto.getDiffieHellman(group_name)#

建立一個預約義的迪菲-赫爾曼密鑰交換(Diffie-Hellman key exchanges)對象。支持如下的D-H組:'modp1''modp2''modp5' (在RFC 2412中定義) 和 'modp14''modp15''modp16''modp17''modp18' (在 RFC 3526中定義)。返回的對象模仿了上述 crypto.createDiffieHellman()方法所建立的對象的接口,但不會暈容許密鑰交換 (例如像 diffieHellman.setPublicKey()那樣)。執行這套流程的好處是雙方不須要事先生成或交換組餘數,節省了處理和通訊時間。

例子 (獲取一個共享祕密):

/* alice_secret和 bob_secret應該是同樣的 */ console.log(alice_secret == bob_secret);

crypto.pbkdf2(password, salt, iterations, keylen, callback)#

異步PBKDF2提供了一個僞隨機函數 HMAC-SHA1,根據給定密碼的長度,salt和iterations來得出一個密鑰。回調函數獲得兩個參數 (err, derivedKey)

crypto.pbkdf2Sync(password, salt, iterations, keylen)#

同步 PBKDF2 函數。返回derivedKey或拋出一個錯誤。

crypto.randomBytes(size, [callback])#

生成密碼學強度的僞隨機數據。用法:

// 同步 try { var buf = crypto.randomBytes(256); console.log('有 %d 字節的隨機數據: %s', buf.length, buf); } catch (ex) { // handle error }

crypto.pseudoRandomBytes(size, [callback])#

生成密碼學強度的僞隨機數據。若是數據足夠長的話會返回一個惟一的數據,但這個返回值不必定是不可預料的。基於這個緣由,當不可預料性很重要時,這個函數的返回值永遠都不該該被使用,例如在生成加密的密鑰時。

用法與 crypto.randomBytes如出一轍。

crypto.DEFAULT_ENCODING#

對於能夠接受字符串或buffer對象的函數的默認編碼方式。默認值是'buffer',因此默認使用Buffer對象。這是爲了讓crypto模塊與默認'binary'爲編碼方式的遺留程序更容易兼容。

要注意,新的程序會期待buffer對象,因此使用這個時請只做爲暫時的手段。

Recent API Changes#

早在統一的流API概念出現,以及引入Buffer對象來處理二進制數據以前,Crypto模塊就被添加到Node。

由於這樣,與流有關的類中並無其它Node類的典型函數,並且不少函數接受和返回默認的二進制編碼的字符串,而不是Buffer對象。在最近的修改中,這些函數都被改爲默認使用Buffer對象。

這對於某些(但不是所有)使用場景來說是重大的改變。

例如,若是你如今使用Sign類的默認參數,而後在沒有檢查數據的狀況下,將結果傳遞給Verify類,那麼程序會照常工做。在之前,你會拿到一個二進制字符串,而後它傳遞給Verify對象;而如今,你會獲得一個Buffer對象,而後把它傳遞給Verify對象。

可是,若是你之前是使用那些在Buffer對象上不能正常工做的字符串數據,或者以默認編碼方式將二進制數據傳遞給加密函數的話,那你就要開始提供編碼方式參數來指明你想使用的編碼方式了。若是想準換回舊的風格默認使用二進制字符串,那麼你須要把crypto.DEFAULT_ENCODING字段設爲'binary'。但請注意,由於新的程序極可能會指望buffer對象,因此僅將此當作臨時手段。

TLS (SSL)#

穩定度: 3 - 穩定

使用 require('tls') 來訪問此模塊。

tls 模塊使用 OpenSSL 來提供傳輸層安全協議(Transport Layer Security)和/或安全套接層(Secure Socket Layer):加密過的流通信。

TLS/SSL 是一種公鑰/私鑰架構。每一個客戶端和服務器都必有一個私鑰。一個私鑰使用相似的方式建立:

openssl genrsa -out ryans-key.pem 1024

全部服務器和某些客戶端須要具有證書。證書是證書辦法機構簽發或自簽發的公鑰。獲取證書的第一步是建立一個「證書籤發申請」(CSR)文件。使用這條命令完成:

openssl req -new -key ryans-key.pem -out ryans-csr.pem

像這樣使用 CSR 建立一個自簽名證書:

openssl x509 -req -in ryans-csr.pem -signkey ryans-key.pem -out ryans-cert.pem

又或者你能夠將 CSR 發送給一個數字證書認證機構請求籤名。

(TODO: docs on creating a CA, for now interested users should just look at test/fixtures/keys/Makefile in the Node source code)

像這樣建立 .pfx 或 .p12:

openssl pkcs12 -export -in agent5-cert.pem -inkey agent5-key.pem \ -certfile ca-cert.pem -out agent5.pfx
  • in: certificate
  • inkey: private key
  • certfile: all CA certs concatenated in one file like cat ca1-cert.pem ca2-cert.pem > ca-cert.pem

Client-initiated renegotiation attack mitigation#

TLS協議會令客戶端能夠從新協商TLS會話的某些方面。可是,會話的從新協商是須要相應量的服務器端資源的,因此致使其變成一個阻斷服務攻擊(denial-of-service)的潛在媒介。

爲了減低這種狀況的發生,從新協商被限制在每10分鐘三次。若是超過這個數目,那麼在tls.TLSSocket實例上就會分發一個錯誤。這個限制是可設置的:

  • tls.CLIENT_RENEG_LIMIT: 從新協商的次數限制,默認爲3。
  • tls.CLIENT_RENEG_WINDOW: 從新協商窗口的秒數,默認爲600(10分鐘)。

除非你徹底理解整個機制和清楚本身要幹什麼,不然不要改變這個默認值。

要測試你的服務器的話,用命令 openssl s_client -connect 地址:端口鏈接上服務器,而後敲擊R<CR>(字母鍵R加回車鍵)幾回。

NPN 和 SNI#

NPN (Next Protocol Negotiation) and SNI (Server Name Indication) are TLS handshake extensions allowing you:

  • NPN - to use one TLS server for multiple protocols (HTTP, SPDY)
  • SNI - to use one TLS server for multiple hostnames with different SSL certificates.

tls.getCiphers()#

返回一個數組,其中包含了所支持的SSL加密器的名字。

示例:

var ciphers = tls.getCiphers(); console.log(ciphers); // ['AES128-SHA', 'AES256-SHA', ...]

tls.createServer(options, [secureConnectionListener])#

新建一個新的 tls.Server. The connectionListener 參數會自動設置爲 secureConnection 事件的監聽器. 這個 options 對象有這些可能性:

  • pfx: 一個String 或Buffer包含了私鑰, 證書和CA certs, 通常是 PFX 或者 PKCS12 格式. (Mutually exclusive with the keycert and ca options.)
  • key: 一個字符串或 Buffer對象,其中包含了PEF格式的服務器的私鑰。 (必需)
  • passphrase: 私鑰或pfx密碼的字符串。
  • cert: A string or Buffer containing the certificate key of the server in PEM format. (Required)
  • ca: An array of strings or Buffers of trusted certificates. If this is omitted several well known "root" CAs will be used, like VeriSign. These are used to authorize connections.
  • crl : Either a string or list of strings of PEM encoded CRLs (Certificate Revocation List)
  • ciphers: A string describing the ciphers to use or exclude.
**NOTE**: Previous revisions of this section suggested `AES256-SHA` as an acceptable cipher. Unfortunately, `AES256-SHA` is a CBC cipher and therefore susceptible to BEAST attacks. Do *not* use it.
  • handshakeTimeout: Abort the connection if the SSL/TLS handshake does not finish in this many milliseconds. The default is 120 seconds.
A `'clientError'` is emitted on the `tls.Server` object whenever a handshake times out.
  • honorCipherOrder : When choosing a cipher, use the server's preferences instead of the client preferences.
Although, this option is disabled by default, it is *recommended* that you use this option in conjunction with the `ciphers` option to mitigate BEAST attacks.
  • requestCert: If true the server will request a certificate from clients that connect and attempt to verify that certificate. Default: false.
  • rejectUnauthorized: If true the server will reject any connection which is not authorized with the list of supplied CAs. This option only has an effect if requestCert is true. Default: false.
  • NPNProtocols: An array or Buffer of possible NPN protocols. (Protocols should be ordered by their priority).
  • SNICallback(servername, cb): A function that will be called if client supports SNI TLS extension. Two argument will be passed to it: servername, and cbSNICallback should invoke cb(null, ctx), where ctx is a SecureContext instance. (You can use crypto.createCredentials(...).context to get proper SecureContext). If SNICallback wasn't provided - default callback with high-level API will be used (see below).
  • sessionTimeout: An integer specifying the seconds after which TLS session identifiers and TLS session tickets created by the server are timed out. See SSL_CTX_set_timeout for more details.
  • sessionIdContext: A string containing a opaque identifier for session resumption. If requestCert is true, the default is MD5 hash value generated from command-line. Otherwise, the default is not provided.
  • secureProtocol: The SSL method to use, e.g. SSLv3_method to force SSL version 3. The possible values depend on your installation of OpenSSL and are defined in the constant SSL_METHODS.

這是一個簡單的應答服務器例子:

var server = tls.createServer(options, function(socket) { console.log('服務器已鏈接', socket.authorized ? '已受權' : '未受權'); socket.write("歡迎!\n"); socket.setEncoding('utf8'); socket.pipe(socket); }); server.listen(8000, function() { console.log('server bound'); });

或者

};
var server = tls.createServer(options, function(socket) { console.log('服務器已鏈接', socket.authorized ? '已受權' : '未受權'); socket.write("歡迎!\n"); socket.setEncoding('utf8'); socket.pipe(socket); }); server.listen(8000, function() { console.log('服務器已綁定'); });

您能夠使用 openssl s_client 鏈接這個服務器來測試:

openssl s_client -connect 127.0.0.1:8000

tls.connect(options, [callback])#

tls.connect(port, [host], [options], [callback])#

Creates a new client connection to the given port and host (old API) or options.port and options.host. (If host is omitted, it defaults to localhost.) options should be an object which specifies:

  • host: Host the client should connect to
  • port: Port the client should connect to
  • socket: Establish secure connection on a given socket rather than creating a new socket. If this option is specified, host and port are ignored.
  • pfx: A string or Buffer containing the private key, certificate and CA certs of the server in PFX or PKCS12 format.
  • key: A string or Buffer containing the private key of the client in PEM format.
  • passphrase: 私鑰或pfx密碼的字符串。
  • cert: A string or Buffer containing the certificate key of the client in PEM format.
  • ca: An array of strings or Buffers of trusted certificates. If this is omitted several well known "root" CAs will be used, like VeriSign. These are used to authorize connections.
  • rejectUnauthorized: If true, the server certificate is verified against the list of supplied CAs. An 'error' event is emitted if verification fails. Default: true.
  • NPNProtocols: An array of string or Buffer containing supported NPN protocols. Buffer should have following format: 0x05hello0x05world, where first byte is next protocol name's length. (Passing array should usually be much simpler: ['hello', 'world'].)
  • servername: Servername for SNI (Server Name Indication) TLS extension.
  • secureProtocol: The SSL method to use, e.g. SSLv3_method to force SSL version 3. The possible values depend on your installation of OpenSSL and are defined in the constant SSL_METHODS.

callback參數會被做爲監聽器添加到'secureConnect'事件。

tls.connect()返回一個tls.TLSSocket對象。

下面是一個上述應答服務器的客戶端的例子:

var socket = tls.connect(8000, options, function() { console.log('client connected', socket.authorized ? 'authorized' : 'unauthorized'); process.stdin.pipe(socket); process.stdin.resume(); }); socket.setEncoding('utf8'); socket.on('data', function(data) { console.log(data); }); socket.on('end', function() { server.close(); });

或者

var socket = tls.connect(8000, options, function() { console.log('client connected', socket.authorized ? 'authorized' : 'unauthorized'); process.stdin.pipe(socket); process.stdin.resume(); }); socket.setEncoding('utf8'); socket.on('data', function(data) { console.log(data); }); socket.on('end', function() { server.close(); });

類: tls.TLSSocket#

Wrapper for instance of net.Socket, replaces internal socket read/write routines to perform transparent encryption/decryption of incoming/outgoing data.

new tls.TLSSocket(socket, options)#

Construct a new TLSSocket object from existing TCP socket.

socket是一個net.Socket示例。

options是一個可能包含如下屬性的對象:

  • credentials: An optional credentials object from crypto.createCredentials( ... )
  • isServer: If true - TLS socket will be instantiated in server-mode

tls.createSecurePair([credentials], [isServer], [requestCert], [rejectUnauthorized])#

穩定性: 0 - 已廢棄。使用 tls.TLSSocket 替代。

Creates a new secure pair object with two streams, one of which reads/writes encrypted data, and one reads/writes cleartext data. Generally the encrypted one is piped to/from an incoming encrypted data stream, and the cleartext one is used as a replacement for the initial encrypted stream.

  • credentials: A credentials object from crypto.createCredentials( ... )
  • isServer: A boolean indicating whether this tls connection should be opened as a server or a client.
  • requestCert: A boolean indicating whether a server should request a certificate from a connecting client. Only applies to server connections.
  • rejectUnauthorized: A boolean indicating whether a server should automatically reject clients with invalid certificates. Only applies to servers with requestCert enabled.

tls.createSecurePair() returns a SecurePair object with cleartext and encrypted stream properties.

NOTE: cleartext has the same APIs as tls.TLSSocket

類: SecurePair#

由tls.createSecurePair返回。

事件: 'secure'#

The event is emitted from the SecurePair once the pair has successfully established a secure connection.

Similarly to the checking for the server 'secureConnection' event, pair.cleartext.authorized should be checked to confirm whether the certificate used properly authorized.

類: tls.Server#

This class is a subclass of net.Server and has the same methods on it. Instead of accepting just raw TCP connections, this accepts encrypted connections using TLS or SSL.

事件: 'secureConnection'#

function (tlsSocket) {}

This event is emitted after a new connection has been successfully handshaked. The argument is a instance of tls.TLSSocket. It has all the common stream methods and events.

socket.authorized is a boolean value which indicates if the client has verified by one of the supplied certificate authorities for the server. If socket.authorized is false, then socket.authorizationError is set to describe how authorization failed. Implied but worth mentioning: depending on the settings of the TLS server, you unauthorized connections may be accepted. socket.npnProtocol is a string containing selected NPN protocol. socket.servername is a string containing servername requested with SNI.

Event: 'clientError'#

function (exception, tlsSocket) { }

When a client connection emits an 'error' event before secure connection is established - it will be forwarded here.

tlsSocket is the tls.TLSSocket that the error originated from.

事件: 'newSession'#

function (sessionId, sessionData) { }

Emitted on creation of TLS session. May be used to store sessions in external storage.

NOTE: adding this event listener will have an effect only on connections established after addition of event listener.

事件: 'resumeSession'#

function (sessionId, callback) { }

Emitted when client wants to resume previous TLS session. Event listener may perform lookup in external storage using given sessionId, and invoke callback(null, sessionData) once finished. If session can't be resumed (i.e. doesn't exist in storage) one may call callback(null, null). Calling callback(err) will terminate incoming connection and destroy socket.

NOTE: adding this event listener will have an effect only on connections established after addition of event listener.

server.listen(port, [host], [callback])#

Begin accepting connections on the specified port and host. If the host is omitted, the server will accept connections directed to any IPv4 address (INADDR_ANY).

This function is asynchronous. The last parameter callback will be called when the server has been bound.

更多信息見net.Server

server.close()#

Stops the server from accepting new connections. This function is asynchronous, the server is finally closed when the server emits a 'close' event.

server.address()#

Returns the bound address, the address family name and port of the server as reported by the operating system. See net.Server.address() for more information.

server.addContext(hostname, credentials)#

Add secure context that will be used if client request's SNI hostname is matching passed hostname (wildcards can be used). credentials can contain keycert and ca.

server.maxConnections#

Set this property to reject connections when the server's connection count gets high.

server.connections#

服務器的併發鏈接數.

類: CryptoStream#

穩定性: 0 - 已廢棄。使用 tls.TLSSocket 替代。

這是一個被加密的流。

cryptoStream.bytesWritten#

A proxy to the underlying socket's bytesWritten accessor, this will return the total bytes written to the socket, including the TLS overhead.

類: tls.TLSSocket#

This is a wrapped version of net.Socket that does transparent encryption of written data and all required TLS negotiation.

This instance implements a duplex Stream interfaces. It has all the common stream methods and events.

事件: 'secureConnect'#

This event is emitted after a new connection has been successfully handshaked. The listener will be called no matter if the server's certificate was authorized or not. It is up to the user to test tlsSocket.authorized to see if the server certificate was signed by one of the specified CAs. If tlsSocket.authorized === false then the error can be found in tlsSocket.authorizationError. Also if NPN was used - you can checktlsSocket.npnProtocol for negotiated protocol.

tlsSocket.authorized#

A boolean that is true if the peer certificate was signed by one of the specified CAs, otherwise false

tlsSocket.authorizationError#

The reason why the peer's certificate has not been verified. This property becomes available only when tlsSocket.authorized === false.

tlsSocket.getPeerCertificate()#

Returns an object representing the peer's certificate. The returned object has some properties corresponding to the field of the certificate.

示例:

{ subject: { C: 'UK', ST: 'Acknack Ltd', L: 'Rhys Jones', O: 'node.js', OU: 'Test TLS Certificate', CN: 'localhost' }, issuer: { C: 'UK', ST: 'Acknack Ltd', L: 'Rhys Jones', O: 'node.js', OU: 'Test TLS Certificate', CN: 'localhost' }, valid_from: 'Nov 11 09:52:22 2009 GMT', valid_to: 'Nov 6 09:52:22 2029 GMT', fingerprint: '2A:7A:C2:DD:E5:F9:CC:53:72:35:99:7A:02:5A:71:38:52:EC:8A:DF' }

If the peer does not provide a certificate, it returns null or an empty object.

tlsSocket.getCipher()#

Returns an object representing the cipher name and the SSL/TLS protocol version of the current connection.

Example: { name: 'AES256-SHA', version: 'TLSv1/SSLv3' }

See SSL_CIPHER_get_name() and SSL_CIPHER_get_version() inhttp://www.openssl.org/docs/ssl/ssl.html#DEALING_WITH_CIPHERS for more information.

tlsSocket.renegotiate(options, callback)#

Initiate TLS renegotiation process. The options may contain the following fields: rejectUnauthorizedrequestCert (See tls.createServer for details). callback(err) will be executed with null as err, once the renegotiation is successfully completed.

NOTE: Can be used to request peer's certificate after the secure connection has been established.

ANOTHER NOTE: When running as the server, socket will be destroyed with an error after handshakeTimeout timeout.

tlsSocket.address()#

Returns the bound address, the address family name and port of the underlying socket as reported by the operating system. Returns an object with three properties, e.g. { port: 12346, family: 'IPv4', address: '127.0.0.1' }

tlsSocket.remoteAddress#

遠程IP地址的字符串表示。例如,'74.125.127.100'或 '2001:4860:a005::68'

tlsSocket.remotePort#

遠程端口的數值表示。例如, 443

tlsSocket.localAddress#

本地IP地址的字符串表達。

tlsSocket.localPort#

本地端口的數值表示。

字符串解碼器#

穩定度: 3 - 穩定

經過 require('string_decoder') 使用這個模塊。這個模塊將一個 Buffer 解碼成一個字符串。他是 buffer.toString() 的一個簡單接口,但提供對 utf8 的支持。

var euro = new Buffer([0xE2, 0x82, 0xAC]); console.log(decoder.write(euro));

類: StringDecoder#

接受 encoding 一個參數,默認是 utf8

decoder.write(buffer)#

返回解碼後的字符串。

decoder.end()#

返回 Buffer 中剩下的末尾字節。

File System#

穩定度: 3 - 穩定

文件系統模塊是一個簡單包裝的標準 POSIX 文件 I/O 操做方法集。您能夠經過調用require('fs')來獲取該模塊。文件系統模塊中的全部方法均有異步和同步版本。 

文件系統模塊中的異步方法須要一個完成時的回調函數做爲最後一個傳入形參。 回調函數的構成由您調用的異步方法所決定,一般狀況下回調函數的第一個形參爲返回的錯誤信息。 若是異步操做執行正確並返回,該錯誤形參則爲null或者undefined

若是您使用的是同步版本的操做方法,則一旦出現錯誤,會以一般的拋出錯誤的形式返回錯誤。 你能夠用trycatch等語句來攔截錯誤並使程序繼續進行。

這裏是一個異步版本的例子:

fs.unlink('/tmp/hello', function (err) { if (err) throw err; console.log('successfully deleted /tmp/hello'); });

這是同步版本的例子:

fs.unlinkSync('/tmp/hello') console.log('successfully deleted /tmp/hello');

當使用異步版本時不能保證執行順序,所以下面這個例子很容易出錯:

fs.rename('/tmp/hello', '/tmp/world', function (err) { if (err) throw err; console.log('renamed complete'); }); fs.stat('/tmp/world', function (err, stats) { if (err) throw err; console.log('stats: ' + JSON.stringify(stats)); });

fs.stat有可能在fs.rename前執行.要等到正確的執行順序應該用下面的方法:

fs.rename('/tmp/hello', '/tmp/world', function (err) { if (err) throw err; fs.stat('/tmp/world', function (err, stats) { if (err) throw err; console.log('stats: ' + JSON.stringify(stats)); }); });

在繁重的任務中,強烈推薦使用這些函數的異步版本.同步版本會阻塞進程,直到完成處理,也就是說會暫停全部的鏈接.

能夠使用文件名的相對路徑, 可是記住這個路徑是相對於process.cwd()的.

大部分的文件系統(fs)函數能夠忽略回調函數(callback)這個參數.若是忽略它,將會由一個默認回調函數(callback)來從新拋出(rethrow)錯誤.要得到原調用點的堆棧跟蹤(trace)信息,須要在環境變量裏設置NODE_DEBUG.

$ env NODE_DEBUG=fs node script.js fs.js:66 throw err; ^ Error: EISDIR, read at rethrow (fs.js:61:21) at maybeCallback (fs.js:79:42) at Object.fs.readFile (fs.js:153:18) at bad (/path/to/script.js:2:17) at Object.<anonymous> (/path/to/script.js:5:1) <etc.>

fs.rename(oldPath, newPath, callback)#

異步版本的rename函數(2).完成時的回調函數(callback)只接受一個參數:可能出現的異常信息.

fs.renameSync(oldPath, newPath)#

同步版本的rename(2).

fs.ftruncate(fd, len, callback)#

異步版本的ftruncate(2). 完成時的回調函數(callback)只接受一個參數:可能出現的異常信息.

fs.ftruncateSync(fd, len)#

同步版本的ftruncate(2).

fs.truncate(path, len, callback)#

異步版本的truncate(2). 完成時的回調函數(callback)只接受一個參數:可能出現的異常信息.

fs.truncateSync(path, len)#

同步版本的truncate(2).

異步版本的chown.完成時的回調函數(callback)只接受一個參數:可能出現的異常信息.

異步版本的chown(2).完成時的回調函數(callback)只接受一個參數:可能出現的異常信息.

fs.chownSync(path, uid, gid)#

同步版本的chown(2).

fs.fchown(fd, uid, gid, callback)#

異步版本的fchown(2)。回調函數的參數除了出現錯誤時有一個錯誤對象外,沒有其它參數。

fs.fchownSync(fd, uid, gid)#

同步版本的fchown(2).

fs.lchown(path, uid, gid, callback)#

異步版的lchown(2)。完成時的回調函數(callback)只接受一個參數:可能出現的異常信息.

fs.lchownSync(path, uid, gid)#

同步版本的lchown(2).

fs.chmod(path, mode, callback)#

異步版的 chmod(2). 完成時的回調函數(callback)只接受一個參數:可能出現的異常信息.

fs.chmodSync(path, mode)#

同步版的 chmod(2).

fs.fchmod(fd, mode, callback)#

異步版的 fchmod(2). 完成時的回調函數(callback)只接受一個參數:可能出現的異常信息.

fs.fchmodSync(fd, mode)#

同步版的 fchmod(2).

fs.lchmod(path, mode, callback)#

異步版的 lchmod(2). 完成時的回調函數(callback)只接受一個參數:可能出現的異常信息.

僅在 Mac OS X 系統下可用。

fs.lchmodSync(path, mode)#

同步版的 lchmod(2).

fs.stat(path, callback)#

異步版的 stat(2). 回調函數(callback) 接收兩個參數: (err, stats) ,其中 stats 是一個 fs.Stats 對象。 詳情請參考 fs.Stats

fs.lstat(path, callback)#

異步版的 lstat(2). 回調函數(callback)接收兩個參數: (err, stats) 其中 stats 是一個 fs.Stats 對象。 lstat() 與 stat() 相同,區別在於: 若 path 是一個符號連接時(symbolic link),讀取的是該符號連接自己,而不是它所 連接到的文件。

fs.fstat(fd, callback)#

異步版的 fstat(2). 回調函數(callback)接收兩個參數: (err, stats) 其中 stats 是一個 fs.Stats 對象。 fstat() 與 stat() 相同,區別在於: 要讀取的文件(譯者注:即第一個參數)是一個文件描述符(file descriptor) fd 。

fs.statSync(path)#

同步版的 stat(2). 返回一個 fs.Stats 實例。

fs.lstatSync(path)#

同步版的 lstat(2). 返回一個 fs.Stats 實例。

fs.fstatSync(fd)#

同步版的 fstat(2). 返回一個 fs.Stats 實例。

fs.link(srcpath, dstpath, callback)#

異步版的 link(2). 完成時的回調函數(callback)只接受一個參數:可能出現的異常信息。

fs.linkSync(srcpath, dstpath)

相關文章
相關標籤/搜索