@xudongh
2017-03-12T11:26:39.000000Z
字数 6115
阅读 1462
前端开发
Node.js REPL(Read Eval Print Loop:交互式解释器) 表示一个电脑的环境,类似 Window 系统的终端或 Unix/Linux shell,可以在终端中输入命令,并接收系统的响应。
可以通过下面的方式来引入它:
const repl = require('repl');
repl 模块导出了 repl.REPLServer 类。 当 repl.REPLServer 的实例运行时,它接收用户输入的每一行,使用用户定义的解释函数解释这些表达式,最终输出结果。
默认的解释器提供了获取所有全局存在的变量的途径。 可以通过给每个 REPLServer 绑定的 context 对象指定变量,来显式地把变量暴露给 REPL。
const repl = require('repl');var msg = 'message';repl.start('> ').context.m = msg;
context对象的属性表现为 REPL 中的本地变量:
$ node repl_test.js> m'message'
默认情况下 context 的属性不是只读的。 如要指定只读的全局变量,context 属性必须使用 Object.defineProperty():
const repl = require('repl');var msg = 'message';const r = repl.start('> ');Object.defineProperty(r.context, 'm', {configurable: false,enumerable: true,value: msg});
默认的解析器会把下划线_解析为最近一次执行的表达式。
> [ 'a', 'b', 'c' ][ 'a', 'b', 'c' ]> _.length3> _ += 14
流(stream)在 Node.js 中是处理流数据的抽象接口(abstract interface)。 stream 模块提供了基础的 API 。使用这些 API 可以很容易地来构建实现流接口的对象。
流可以是可读的、可写的,或是可读写的。所有的流都是 EventEmitter 的实例。
stream 模块可以通过以下方式引入:
const stream = require('stream');
所有的Stream对象都是EventEmitter的实例,常用的事件有:
例:
创建input.txt文件,内容如下:
123
创建main.js文件,代码如下:
var fs = require("fs");var data = '';// 创建可读流var readerStream = fs.createReadStream('input.txt');// 设置编码为 utf8。readerStream.setEncoding('UTF8');// 处理流事件 --> data, end, and errorreaderStream.on('data', function(chunk) {data += chunk;});readerStream.on('end',function(){console.log(data);});readerStream.on('error', function(err){console.log(err.stack);});console.log("程序执行完毕");
代码运行结果:
程序执行完毕123
创建main.js文件,代码:
var fs = require("fs");var data = '123';// 创建一个可以写入的流,写入到文件 output.txt 中var writerStream = fs.createWriteStream('output.txt');// 使用 utf8 编码写入数据writerStream.write(data,'UTF8');// 标记文件末尾writerStream.end();// 处理流事件 --> data, end, and errorwriterStream.on('finish', function() {console.log("写入完成。");});writerStream.on('error', function(err){console.log(err.stack);});console.log("程序执行完毕");
代码运行结果为创建了output.txt文件,output.txt文件内容为:
123
管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。
input.txt文件内容如下:
123
创建main.js文件,代码如下:
var fs = require("fs");// 创建一个可读流var readerStream = fs.createReadStream('input.txt');// 创建一个可写流var writerStream = fs.createWriteStream('output.txt');// 管道读写操作// 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中readerStream.pipe(writerStream);console.log("程序执行完毕");
代码执行后,创建了output.txt文件,内容为:
123
链式是通过连接输出流到另外一个流并创建多个对个流操作链的机制。链式流一般用于管道操作。
1.压缩文件
var fs = require("fs");var zlib = require('zlib');// 压缩 input.txt 文件为 input.txt.gzfs.createReadStream('input.txt').pipe(zlib.createGzip()).pipe(fs.createWriteStream('input.txt.gz'));console.log("文件压缩完成。");
代码执行完后,在当前目录下生成了input.txt的压缩文件input.txt.gz。
2.解压文件
var fs = require("fs");var zlib = require('zlib');// 解压 input.txt.gz 文件为 input.txtfs.createReadStream('input.txt.gz').pipe(zlib.createGunzip()).pipe(fs.createWriteStream('input.txt'));console.log("文件解压完成。");
Buffer数据类型用于处理二进制数据流。Buffer的大小在其创建时就已确定,且不能调整大小。
Buffer类是在Node.js中是一个全局变量,因此无需require('buffer').Buffer。
// 创建一个长度为 10、且用 0 填充的 Buffer。const buf1 = Buffer.alloc(10);// 创建一个长度为 10、且用 0x1 填充的 Buffer。const buf2 = Buffer.alloc(10, 1);// 创建一个长度为 10、且未初始化的 Buffer。// 这个方法比调用 Buffer.alloc() 更快,// 但返回的 Buffer 实例可能包含旧数据,// 因此需要使用 fill() 或 write() 重写。const buf3 = Buffer.allocUnsafe(10);// 创建一个包含 [0x1, 0x2, 0x3] 的 Buffer。const buf4 = Buffer.from([1, 2, 3]);// 创建一个包含 ASCII 字节数组 [0x74, 0x65, 0x73, 0x74] 的 Buffer。const buf5 = Buffer.from('test');// 创建一个包含 UTF-8 字节数组 [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer。const buf6 = Buffer.from('tést', 'utf8');
Node.js 可以在一开始就使用 --zero-fill-buffers 命令行选项强制所有使用 new Buffer(size) 、Buffer.allocUnsafe() 、Buffer.allocUnsafeSlow() 或 new SlowBuffer(size) 新分配的 Buffer 实例在创建时自动用 0 填充。
使用这个选项会改变这些方法的默认行为,且对性能有明显的影响。
建议只在需要强制新分配的 Buffer 实例不能包含潜在的敏感数据时才使用 --zero-fill-buffers 选项。
$ node --zero-fill-buffers> Buffer.allocUnsafe(5);<Buffer 00 00 00 00 00>
通过使用显式的字符编码,就可以在 Buffer 实例与普通的 JavaScript 字符串之间进行相互转换。
Buffer支持的字符编码:
例:
const buf = Buffer.from('hello world', 'ascii');console.log(buf.toString());//hello worldconsole.log(buf.toString('hex'));//68656c6c6f20776f726c64
<Integer> 新建的 Buffer 开始的位置。 默认: 0<Integer> 新建的 Buffer 结束的位置(不包含)。 默认: buf.length<Buffer>
const buf1 = Buffer.allocUnsafe(26);for (var i = 0 ; i < 26 ; i++) {// 97 是 'a' 的十进制 ASCII 值buf1[i] = i + 97;}const buf2 = buf1.slice(0, 3);// 输出: abcconsole.log(buf2.toString('ascii', 0, buf2.length));buf1[0] = 33;// 输出: !bcconsole.log(buf2.toString('ascii', 0, buf2.length));
const str = '\u00bd + \u00bc = \u00be';// 输出: ½ + ¼ = ¾: 9 个字符, 12 个字节console.log(`${str}: ${str.length} 个字符, ` +`${Buffer.byteLength(str, 'utf8')} 个字节`);//字节:一个汉字2字节、全角标点2字节、半角标点1字节、字母1字节//字符:一个汉字、标点、字母都是一个字符
所有能触发事件的对象都是 EventEmitter 类的实例。
这些对象开放了一个 eventEmitter.on() 函数,允许将一个或多个函数附加到会被对象触发的命名事件上。
以下例子展示了一个只有单个监听器的 EventEmitter 实例。 eventEmitter.on() 方法用于注册监听器,eventEmitter.emit() 方法用于触发事件。
const EventEmitter = require('events');class MyEmitter extends EventEmitter {}const myEmitter = new MyEmitter();myEmitter.on('event', () => {console.log('发生了一个事件!');});myEmitter.emit('event');
EventListener 会按照监听器注册的顺序同步地调用所有监听器。 所以需要确保事件的正确排序且避免竞争条件或逻辑错误。 监听器函数可以使用 setImmediate() 或 process.nextTick() 方法切换到异步操作模式:
const myEmitter = new MyEmitter();myEmitter.on('event', (a, b) => {setImmediate(() => {console.log('这个是异步发生的');});});myEmitter.emit('event', 'a', 'b');
当 EventEmitter 实例中发生错误时,会触发一个 'error' 事件。
如果 EventEmitter 没有为 'error' 事件注册至少一个监听器,则当 'error' 事件触发时,会抛出错误、打印堆栈跟踪、且退出 Node.js 进程。
const myEmitter = new MyEmitter();myEmitter.emit('error', new Error('whoops!'));// 抛出错误,并使 Node.js 奔溃//因此,应该始终为'error'事件注册监听器
EventEmitter 类由 events 模块定义和开放的:
const EventEmitter = require('events');