Node.js回调
Node.js异步编程的直接体现就是回调。
阻塞代码:
const fs = require("fs");
let data = fs.readFileSync('input.txt');
console.log(data.toString());
console.log("程序执行结束");
非阻塞代码:
const fs = require("fs");
fs.readFile('input.txt',(error,data)=>{
if(error) return console.log(error);
console.log(data.toString());
});
console.log("程序执行结束");
Node.js事件循环
Node.js是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高。
进程:CPU执行任务的模块。线程:模块中的最小单元。
Node.js使用事件驱动模型。
事件就是需要 eventEmitter.on 去绑定一个事件 通过 eventEmitter.emit 去触发这个事件其次说的是 事件的 接收 和 发生 是分开的
代码如下:
// 引入events 模块
const events = require('events');
// 创建 eventEmitter 对象
let eventEmitter = new events.EventEmitter();
// 这里既可以使用.on 也可以使用 .addListener
eventEmitter.on('dataReceived',(data) => {
console.log(`接受的数据为:${data}`);
});
// 触发 dataReceived 事件
eventEmitter.emit('dataReceived');
继承 EventEmitter
大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。
若事件队列中出现一个未绑定事件则触发error事件,若未绑定 error事件则程序抛出异常结束执行 emitter.emit('error'); 程序会直接中断,并抛出异常。
Node.js Buffer缓冲区
该类用来创建一个专门存放二进制数据的缓存区。
在v6.0以后,官方文档里面建议使用 Buffer.from() 接口去创建Buffer对象。
Buffer与字符串编码
Buffer 实例一般用于表示编码字符的序列,比如 UTF-8 、 UCS2 、 Base64 、或十六进制编码的数据。 通过使用显式的字符编码,就可以在 Buffer 实例与普通的 JavaScript 字符串之间进行相互转换。
const buf = Buffer.from('hello','utf8');
// 输出 hex 编码后的内容
console.log(buff.toString('hex'));
// 输出 base64 编码后的内容
console.log(buf.toString('base64'));
创建 Buffer 类
通过 from 和 allocation(n:分配)
// 创建一个长度为 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。
// 如果是字符串会被 0 覆盖
const buf4 = Buffer.from([1, 2, 3]);
// 创建一个包含 UTF-8 字节 [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer。
const buf5 = Buffer.from('tést');
// 创建一个包含 Latin-1 字节 [0x74, 0xe9, 0x73, 0x74] 的 Buffer。
const buf6 = Buffer.from('tést', 'latin1');
写入缓冲区
buf.write(string,[offset],[length],[encoding]);
参数描述:
- string - 写入缓冲区的字符串。
- offset -开始写入的索引值,默认为0。
- length -写入的字节数,默认为buffer.length。
- encoding -使用的字符编码,默认为‘utf8’。
// 分配一个256字节的缓冲区
let buf = Buffer.alloc(256);
let len = buf.write("www.baidu.com");
console.log(`写入的字节数数为:${len}`);
缓冲区读取数据
buf.toString([encoding],[start],[end]);
参数描述:
- encoding - 使用的编码。默认为 'utf8' 。
- start - 指定开始读取的索引位置,默认为 0。
- end - 结束位置,默认为缓冲区的末尾。
// 创建缓冲区并写入26个小写英文字母的ASCII码
buf = Buffer.alloc(26);
for (var i = 0 ; i < 26 ; i++) {
buf[i] = i + 97;
}
console.log( buf.toString('ascii')); // 输出: abcdefghijklmnopqrstuvwxyz
console.log( buf.toString('ascii',0,5)); // 输出: abcde
console.log( buf.toString('utf8',0,5)); // 输出: abcde
console.log( buf.toString(undefined,0,5)); // 使用 'utf8' 编码, 并输出: abcde
Buffer 转换为 JSON 对象
buf.toJSON();
缓冲区合并
var buffer3 = Buffer.concat([buffer1,buffer2]);
拷贝缓冲区
buf.copy([targetBufffer],[targetStart],[sourceStart],{sourceEnd});
eg: buf2.copy(buf1,2) // 将buf2插入到buf1指定的位置上
Node.js Stream(流)
Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出)。
所有的Stream对象继承了 EventEmitter 类,所以拥有以下事件:
- data - 当有数据可读时触发。
- end - 没有更多的数据可读时触发。
- error - 在接收和写入过程中发生错误时触发。
- finish - 所有数据已被写入到底层系统时触发。
从流中读取数据
const fs = require("fs");
let data = "";
// 创建可读流
let readStream = fs.createReadStream('input.txt');
readStream.on('data',(chunk)=>{
data += chunk;
})
readStream.on('end',()=>{
console.log(data);
})
...
写入流
var fs = require("fs");
var data = '百度官网地址:www.baidu.com';
// 创建一个可以写入的流,写入到文件 output.txt 中
var writerStream = fs.createWriteStream('output.txt');
// 使用 utf8 编码写入数据
writerStream.write(data,'UTF8');
// 标记文件末尾
writerStream.end();
// 处理流事件 --> data, end, and error
writerStream.on('finish', ()=> {
console.log("写入完成。");
});
管道流
可以实现大文件的复制。
const fs = require('fs');
let rs = fs.createReadStream('input.txt');
let ws = fs.createWriteStream('output.txt');
// 管道读写操作
// 读取input.txt,将内容写入到output.txt文件中
rs.pipe(ws);
链式流
链式是通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作。
接下来我们就是用管道和链式来压缩和解压文件。
var fs = require("fs");
var zlib = require('zlib');
// 压缩 input.txt 文件为 input.txt.gz
fs.createReadStream('input.txt')
.pipe(zlib.createGzip())
.pipe(fs.createWriteStream('input.txt.gz'));
console.log("文件压缩完成。");
Node.js全局对象
Node.js 中的全局对象是 global,所有全局变量(除了 global 本身以外)都是 global 对象的属性。
__filename :表示当前正在执行的脚本的文件名。(输出文件所在的绝对路径)
__ __dirnname __:表示当前执行脚本呢所在的目录。
__ process __:它用于描述当前Node.js 进程状态的对象。
Node.js常用工具
util是一个node。js核心模块,提供常用的函数的集合。
- util.inherits(constructor,superConstructor) 是一个实现对象间原型继承的函数。(构造函数中的属性和函数不会被继承)。
- util.inspect(object,[showHidden],[depth],[colors]) 是一个将任意对象转换 为字符串的方法,通常用于调试和错误输出。
- util.isArray(object) 判断是否为一个数组。
- util.isRegExp(object) 判断是否为一个正则表达式。
- util.isDate(object) 判断是否为一个日期。
Node.js 文件系统
Node.js 提供一组标准的文件操作API。
var fs = require("fs");
打开文件
fs.open(path,flags,[mode],callback);
- path -文件路径。
- flags -文件打开的行为。(r r+ rs[同步] w a[追加]...)
- mode - 设置文件模式(权限),文件创建默认权限为 0666(可读,可写)。
- callback - 回调函数,带有两个参数如:callback(err, fd)。
获取文件信息
以下为通过异步模式获取文件信息的语法格式:
fs.stat(path,callback) // 带两个参数:(err,stats)
可以通过stats类中的提供方法判断文件的相关属性。
- stats.isFile() 如果是文件返回 true,否则返回 false。
- stats.isDirectory() 如果是目录返回 true,否则返回 false。
- stats.isBlockDevice() 如果是块设备返回 true,否则返回 false。
- stats.isCharacterDevice() 如果是字符设备返回 true,否则返回 false。
- stats.isSymbolicLink() 如果是软链接返回 true,否则返回 false。
- stats.isFIFO() 如果是FIFO,返回true,否则返回 false。FIFO是UNIX中的一种特殊类型的命令管道。
- stats.isSocket() 如果是 Socket 返回 true,否则返回 false。
写入文件
以下为异步模式下写入文件的语法格式:
fs.writeFile(file, data,[options], callback)
如果文件存在,该方法写入的内容会覆盖旧的文件内容。
参数使用说明如下:
- file - 文件名或文件描述符。
- data - 要写入文件的数据,可以是 String(字符串) 或 Buffer(流) 对象。
- options - 该参数是一个对象,包含 {encoding, mode, flag}。默认编码为 utf8, 模式为 0666 , flag 为 'w'
- callback - 回调函数,回调函数只包含错误信息参数(err),在写入失败时返回。
读取文件
以下为异步模式下读取文件的语法格式:
fs.read(fd, buffer, offset, length, position, callback)
该方法使用了文件描述符来读取文件。
参数使用说明如下:
- fd - 通过 fs.open() 方法返回的文件描述符。
- buffer - 数据写入的缓冲区。
- offset - 缓冲区写入的写入偏移量。
- length - 要从文件中读取的字节数。
- position - 文件读取的起始位置,如果 position 的值为 null,则会从当前文件指针的位置读取。
- callback - 回调函数,有三个参数err, bytesRead, buffer,err 为错误信息, + bytesRead 表示读取的字节数,buffer 为缓冲区对象。
关闭文件
fs.close(fd,callback)
打开文件,读取文件,关闭文件示例代码:
var fs = require("fs");
var buf = new Buffer(1024);
console.log("准备打开文件!");
fs.open('input.txt', 'r+', function(err, fd) {
if (err) {
return console.error(err);
}
console.log("文件打开成功!");
console.log("准备读取文件!");
fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){
if (err){
console.log(err);
}
// 仅输出读取的字节
if(bytes > 0){
console.log(buf.slice(0, bytes).toString());
}
// 关闭文件
fs.close(fd, function(err){
if (err){
console.log(err);
}
console.log("文件关闭成功");
});
});
});
删除文件
fs.unlink(path,callback);
创建目录
fs.mkdir(path,[mode],callback);
读取目录
fs.readdir(path,callback); // callback(err,files[文件列表])
删除目录
fs.rmdir(path,callback);
Node.js 模块
os模块
os模块提供了一些基本的系统操作函数。
var os = require("os");
// CPU 的字节序
console.log('endianness : ' + os.endianness());
// 操作系统名
console.log('type : ' + os.type());
// 操作系统名
console.log('platform : ' + os.platform());
// 系统内存总量
console.log('total memory : ' + os.totalmem() + " bytes.");
// 操作系统空闲内存量
console.log('free memory : ' + os.freemem() + " bytes.");
path模块
var path = require("path");
// 格式化路径
console.log('normalization : ' + path.normalize('/test/test1//2slashes/1slash/tab/..')); // normalization : /test/test1/2slashes/1slash
// 连接路径
console.log('joint path : ' + path.join('/test', 'test1', '2slashes/1slash', 'tab', '..')); // joint path : /test/test1/2slashes/1slash
// 转换为绝对路径
console.log('resolve : ' + path.resolve('main.js')); // resolve : /web/com/1427176256_27423/main.js
// 路径中文件的后缀名
console.log('ext name : ' + path.extname('main.js')); // ext name : .js
DNS模块
dns.lookup(hostname,[options(4|6)],callback); // 解析域名
var dns = require('dns');
dns.lookup('www.github.com', function onLookup(err, address, family) {
console.log('ip 地址:', address);
dns.reverse(address, function (err, hostnames) {
if (err) {
console.log(err.stack);
}
console.log('反向解析 ' + address + ': ' + JSON.stringify(hostnames));
});
});
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。