2

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));
   });  
});

五月花开
1.3k 声望878 粉丝

仰望星空,也要脚踏实地。