zoukankan      html  css  js  c++  java
  • Node.js躬行记(1)——Buffer、流和EventEmitter

    一、Buffer

      Buffer是一种Node的内置类型,不需要通过require()函数额外引入。它能读取和写入二进制数据,常用于解析网络数据流、文件等。

    1)创建

      通过new关键字初始化Buffer对象的方式已经被废弃,下面的代码都已经过时。

    new Buffer(array)
    new Buffer(arrayBuffer[, byteOffset[, length]])
    new Buffer(buffer)
    new Buffer(size)
    new Buffer(string[, encoding])

      目前有两种方式创建Buffer对象,第一种是通过alloc()或allocUnsafe()两个静态方法,语法如下。

    Buffer.alloc(size[, fill[, encoding]])
    Buffer.allocUnsafe(size)

      alloc()方法可接收三个参数,后两个是可选的。第一个参数是长度,第二个参数是预填充的值,第三个参数是字符编码,默认值是“utf8”。

    const buf1 = Buffer.alloc(10);
    const buf2 = Buffer.alloc(10, "A");
    const buf3 = Buffer.alloc(10, "A", "ascii");

      如果打印buf3,那么得到的将是一组十六进制数据,而非难以阅读的二进制数据,如下所示。

    <Buffer 41 41 41 41 41 41 41 41 41 41>

      注意,Buffer的大小在创建时确定,后面也无法更改,其类型可由Buffer.isBuffer()辨别。当前Node支持的字符编码有6种:

      (1)ascii:仅适用于7位的ASCII数据。

      (2)utf8:多字节编码的Unicode字符。许多网页和其它文档格式都在使用UTF-8。

      (3)utf16le/ucs2:2或4个字节,小端序编码的Unicode字符。支持代理对(U+10000至U+10FFFF)。

      (4)base64:Base64编码,一种基于64个可打印字符来表示二进制数据的表示方法。

      (5)latin1/binary:一种可编码成单字节字符串的方法。

      (6)hex:将每个字节编码成两个十六进制的字符。

      allocUnsafe()是一个不安全的方法,因为它分配的内存片段是未初始化的,即没有被清零。虽然这种设计性能优越,但分配的内存中可能会包含旧数据。

      第二种是通过Buffer.from()方法创建Buffer对象,它的参数可以是数组、字符串、Buffer对象等,如下所示。

    const buf = Buffer.from("A");
    console.log(buf);        //<Buffer 41>

    2)转换编码

      在读取文件时,可通过toString()方法将Buffer对象转换成字符串,如下所示,默认是UTF-8格式。

    const fs = require('fs');
    fs.readFile('./demo.txt', (err, buf) => {
      buf.toString();              //"你好,Node.js"
      buf.toString("base64");       //"5L2g5aW977yMTm9kZS5qcw=="
    });

    二、流

      Node中的stream模块用于处理流式数据,许多内置的核心模块都在其内部实现了流操作,流还适用于网络传输、JSON解析器、RFC(远程调用)等。流包括四个抽象类:

      (1)Readable:可读流,读取底层的I/O数据源。

      (2)Writeable:可写流,将数据写入到目标中。

      (3)Duplex:双工流,即可读也可写。

      (4)Transform:转换流,会修改数据的双工流。

    1)pipe()

      在可读流中,包含一个管道方法:pipe(),它的作用是关联可读流与可写流,让数据通过管道从可读流进入到可写流中。pipe()方法能接收一个Writable对象,并返回对目标流的引用,从而可形成链式调用。

      在下面的示例中,会将origin.txt中的数据通过管道写入到target.txt文件中,调用文件模块的createReadStream()方法能得到一个Readable对象。

    const fs = require('fs');
    const readable = fs.createReadStream('./origin.txt');
    const writable = fs.createWriteStream('./target.txt');
    readable.pipe(writable);

    2)事件

      以可读流为例,它的data事件可在接收到数据块后触发,而end事件会在流没有数据时触发。在下面的示例中,origin.txt文件包含的内容是“hello Node.js”。

    const fs = require('fs');
    const readable = fs.createReadStream('./origin.txt', {highWaterMark: 2});
    readable.on("data", (chunk) => {
      console.log(`接收到 ${chunk.length} 个字节的数据`, chunk.toString());
    });
    readable.on("end", () => {
      console.log("结束接收");
    });

      在调用createReadStream()方法时,包含一个highWaterMark属性,其默认值为64KB,它的作用是限制可缓冲的字节数。当定义为2后,每接收2个字节的数据,就会触发data事件,打印结果如下所示。

    接收到 2 个字节的数据 he
    接收到 2 个字节的数据 ll
    接收到 2 个字节的数据 o
    接收到 2 个字节的数据 No
    接收到 2 个字节的数据 de
    接收到 2 个字节的数据 .j
    接收到 1 个字节的数据 s
    结束接收

      可读流还包含一个error事件,用于监听异常,其事件处理程序会接收一个Error对象。在下面的示例中,会读取不存在的文件,从而触发error事件。

    const readable = fs.createReadStream('./demo.txt');
    readable.on("error", (err) => {
      console.log(err);        //打印错误信息
    });

    3)实现流

      当实现自定义的流时,需要继承四个抽象类中的一个,表1列出了四个抽象类需要实现的方法。

    抽象类 需要实现的方法
    Readable _read()
    Writeable _write()、_writev()、_final()
    Duplex _read()、_write()、_writev()、_final()
    Transform _transform()、_flush()、_final()

      下面是一个自定义可写流的例子,_write()方法中的encoding是一个字符串,表示字符编码。

    const { Writable } = require('stream');
    class MyWritable extends Writable {
      constructor(options) {
        super(options);
      }
      _write(chunk, encoding, callback) {
        if (encoding === "buffer") {
          callback();
        }
      }
    }

    三、EventEmitter

      Node的事件模块目前只包含一个EventEmitter类(即事件触发器),所有能触发事件的对象都是EventEmitter类的实例。EventEmitter通常被用作基类,在Node内部,凡是提供事件机制的模块都会继承它。

      在下面的示例中,声明了一个EventEmitter实例,on()方法用于注册监听器,emit()方法用于触发事件。在调用emit()方法时,传递了自定义的type参数。

    const EventEmitter = require('events');
    class MyEmitter extends EventEmitter {}
    const myEmitter = new MyEmitter();
    
    myEmitter.on('click', (type) => {
      console.log(`触发${type}事件`);
    });
    myEmitter.emit('click', "点击");

      注意,可注册多个相同名称的事件,监听器会按照添加顺序依次调用。事件模块还提供了很多其它方法,例如off()用于解除事件绑定,once()可以只监听一次事件。

  • 相关阅读:
    20170226-云计算设计模式翻译-自动伸缩指南(逐字翻译)
    20170723-Ioc与AOP
    20170710-几维晨规
    诸城项目-开发日志
    GPS常识-B版(简)
    GPS常识-A版(详)
    20141209-基本概念-BlogEngine.NET(1)-笔记
    9.聚类分析
    7.分类:基本概念 忌讳
    6.挖掘关联规则
  • 原文地址:https://www.cnblogs.com/strick/p/12626335.html
Copyright © 2011-2022 走看看