nodejs api 中文文档

2019年11月16日 阅读数:117
这篇文章主要向大家介绍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)