Node.js 入门

作者:刘专,日期:2017 年 12 月 19 日

Node.js 是一个基于谷歌 Chrome V8 引擎的 JavaScript 框架,常用于后端开发和自动化脚本工具。

Node.js 生于 2009 年,生父 Ryan Dahl,养父 Isaac Schlueter

相关代码可查看node-demo

事件循环

Node.js 是单线程应用,但通过事件和回调函数可支持并发执行。

var events = require('events')

var eventEmitter = new events.EventEmitter()
eventEmitter.on('eventName', eventHandler)
eventEmitter.emit('eventName')

Node 的很多对象都可以抛出事件,它们都是 events.EventEmitter 的实例。

EventEmitter 对象的方法包括:

// 实例方法
addListener(event, listener)
on(event, listener)
once(event, listener)
removeListener(event, listener)
removeAllListener([event])
setMaxListeners(n)
// 返回指定事件的监听器数组
listeners(event)
emit(event, [arg1], [arg2], [...])

// 类方法
EventEmitter.listenerCount(emitter, event)

EventEmitter 自带的事件有 newListener, removeListener

event-emitter code 可以看出,on 不检测监听函数是否已存在,会有重复调用状况。

缓冲区 Buffer

纯 JavaScript 对二进制支持有限。但在 TCP 流或文件系统中,经常需要处理八位组。Node.js 因此提供 Buffer 类,专门存储原始数据,就像整型数组一样。Buffer 实例对应 v8 堆之外一块裸内存。

Buffer 是全局类,无需导入,开箱即用。

/** 创建缓冲区 */
// 创建 10 个未初始化八位组
var buf = new Buffer(10)
// 从数组创建
var buf = new Buffer([10, 20, 30, 40, 50])
// 从给定字符串创建,可选编码
var buf = new Buffer('Simply Easy Learning', 'utf-8')
// 可选编码有 utf8, ascii, utf8, utf16le, ucs2, base64, hex

/** 向缓冲区写 */
buf.write(string[, offset][, length][, encoding])
// 向缓冲区写入字符串,若字符字数超过缓冲区长度,会被截断

/** 读取缓冲区 */
buf.toString([encoding][, start][, end])

/** 缓冲区转换为 JSON */
buf = new Buffer('hello')
buf.toJSON()
// => { type: 'Buffer', data: [ 104, 101, 108, 108, 111 ] }

/** 连接缓冲区 */
Buffer.concat(list[, totalLength])
var buffer1 = new Buffer('TutorialsPoint')
var buffer2 = new Buffer('Simply Easy Learning')
var buffer3 = Buffer.concat([buffer1, buffer2])
console.log('buffer3 content: ' + buffer3.toString())

/** 比较缓冲区 */
buf.compare(otherBuffer)

/** 拷贝缓冲区 */
buf.copy(targetBuffer[, targetStart][, sourceStart][, sourceEnd])

/** 切片缓冲区 */
buf.slice([start][, end])

/** 类方法 */
Buffer.isEncoding(encoding)
Buffer.isBuffer(obj)
Buffer.byteLength(string[, encoding])
Buffer.concat(list[, totalLength])
Buffer.compare(buf1, buf2)

流可以无间断读取或写入数据,有四种类型:

流皆为 EventEmitter 实例,可在不同阶段抛事件。常用事件如下:

常用 API 如下:

/** 创建可读流 */
var readStream = fs.createReadStream(file)
readStream.setEncoding('utf8')
readStream.on('data', (chunk) => data += chunk)
readStream.on('end', () => console.log(data))
readStream.on('error', err => console.log(err.stack))

/** 创建可写流 */
var writeStream = fs.createWriteStream('output.txt')
writeStream.write(data, 'utf8')
// 标记文件结束
writeStream.end()

writeStream.on('finish', () => console.log('write complete'))
writeStream.on('error', err => console.log(err.stack))

/** 管道链接流 */
var readStream = fs.createReadStream('input.txt')
var writeStream = fs.createWriteStream('output.txt')
readStream.pipe(writeStream)

/** 串联流 */
fs.createReadStream('input.txt')
    .pipe(zlib.createGzip())
    .pipe(fs.createWriteStream('input.txt.gz'))

流代码示例

文件系统

Node 文件处理是标准 POSIX 函数的简单封装,在 fs 模块定义。

fs 模块每个方法都有同步和异步版本。

常用 API 如下:

var fs = require('fs')

fs.readFile('input.txt', function(err, data){
    if (err) {
        return console.error(err)
    }
    console.log('Asynchronous read: ' + data.toString())
})

/** 打开文件 */
fs.open(path, flags[, mode], callback)

/** 获取文件信息 */
fs.stat(path, function(err, stats){})

// fs.Stats 函数
stats.isFile()
stats.isDirectory()
stats.isBlockDevice()
stats.isCharacterDevice()
stats.isSymbolicLink()
stats.isFIFO()
stats.isSocket()

/** 写文件 */
fs.writeFile(filename, data[, options], callback)

/** 读取文件 */
fs.read(fd, buffer, offset, length, position, callback)
// fd: 文件描述符,fs.open() 返回
// buffer: 储存数据的缓冲区
// offset: 写入数据位于缓冲区的偏移量
// length: 写入字节的数目
// position: 从文件哪个位置开始读取
// callback: 回调函数,形参有三:err, bytesRead, buffer

/** 关闭文件 */
fs.close(fd, callback)

/** 截短文件 */
fs.ftruncate(fd, len, callback)

/** 删除文件 */
fs.unlink(path, callback)

/** 创建目录 */
fs.mkdir(path[, mode], callback)

/** 读取目录 */
fs.readdir(path, function(err, files){})

/** 删除目录 */
fs.rmdir(path, callback)

常见的 flags 如下:

工具模块

OS 模块

Path 模块

Net 模块

DNS 模块

Domain 模块

TOREAD: https://www.tutorialspoint.com/nodejs/nodejs_utitlity_module.htm

术语表

REF