毕竟不是一个真正的教程,这里主要还是以普及和介绍为主,所以这一部分就是 Node.js 的其他部分介绍了,主要也就是事件触发、操作系统以及流的知识。


1 事件触发器

因为我们之前在浏览器中使用 JavaScript,所以知道 JS 通过事件处理了许多用户的交互:鼠标的单击、键盘按钮的按下、对鼠标移动的反应等等。

在后端,Node.js 也提供了使用 events 模块 构建类似系统的选项。

具体上,此模块提供了 EventEmitter 类,用于处理事件。

使用以下代码进行初始化:

const EventEmitter = require('events');const eventEmitter = new EventEmitter();

该对象公开了 onemit 方法:

  • emit 用于触发事件
  • on 用于添加回调函数(会在事件被触发时执行)

例如,创建 start 事件,并提供一个示例,通过记录到控制台进行交互:

eventEmitter.on('start', () => {console.log('开始');
});

当运行以下代码时:

eventEmitter.emit('start');

事件处理函数会被触发,且获得控制台日志。

可以通过将参数作为额外参数传给 emit() 来将参数传给事件处理程序:

eventEmitter.on('start', number => {console.log(`开始 ${number}`);
});
eventEmitter.emit('start', 23);

多个参数:

eventEmitter.on('start', (start, end) => {console.log(`从 ${start} 到 ${end}`);
});
eventEmitter.emit('start', 1, 100);

EventEmitter 对象还公开了其他几个与事件进行交互的方法,例如:

  • once():添加单次监听器
  • removeListener() / off():从事件中移除事件监听器
  • removeAllListeners():移除事件的所有监听器

下面详细的介绍一下事件模块:

2 事件模块

events 模块为提供了 EventEmitter 类,这是在 Node.js 中处理事件的关键,如上文简单的引入 events 即可使用,这里是另一个例子:

const EventEmitter = require('events');
const door = new EventEmitter();

事件监听器返回及使用以下事件:

  • 当监听器被添加时返回 newListener
  • 当监听器被移除时返回 removeListener

以下是最常用的方法的详细说明:

2.1 emitter.addListener()

emitter.on() 的别名,这是为了和 DOM API 保持一定的一致([DOM].addEventListener())。

2.2 emitter.emit()

触发事件, 按照事件被注册的顺序同步地调用每个事件监听器:

door.emit("slam"); // 触发 "slam" 事件。

2.3 emitter.eventNames()

返回字符串(表示在当前 EventEmitter 对象上注册的事件)数组:

door.eventNames();

2.4 emitter.getMaxListeners()

获取可以添加到 EventEmitter 对象的监听器的最大数量(默认为 10,但可以使用 setMaxListeners() 进行增加或减少)。

door.getMaxListeners();

2.5 emitter.listenerCount()

获取作为参数传入的事件监听器的计数:

door.listenerCount('open');

2.6 emitter.listeners()

获取作为参数传入的事件监听器的数组:

door.listeners('open');

2.7 emitter.off()

emitter.removeListener() 的别名,新增于 Node.js 10;这是为了和 emitter.on() 形成对应,简化记忆。

2.8 emitter.on()

添加当事件被触发时调用的回调函数,用法:

door.on('open', () => { console.log('打开');
});

2.9 emitter.once()

添加当事件在注册之后首次被触发时调用的回调函数, 该回调只会被调用一次,不会再被调用:

const EventEmitter = require('events');
const ee = new EventEmitter();ee.once('my-event', () => {//只调用一次回调函数
});

2.10 emitter.prependListener()

当使用 onaddListener 添加监听器时,监听器会被添加到监听器队列中的最后一个,并且最后一个被调用; 使用 prependListener 则可以在其他监听器之前添加并调用。

2.11 emitter.prependOnceListener()

当使用 once 添加监听器时,监听器会被添加到监听器队列中的最后一个,并且最后一个被调用; 使用 prependOnceListener 则可以在其他监听器之前添加并调用。

2.12 emitter.removeAllListeners()

移除 EventEmitter 对象的所有监听特定事件的监听器:

door.removeAllListeners('open');

2.13 emitter.removeListener()

移除特定的监听器,可以通过将回调函数保存到变量中(当添加时),以便以后可以引用它:

const doSomething = () => {};door.on('open', doSomething);
door.removeListener('open', doSomething);

2.14 emitter.setMaxListeners()

设置可以添加到 EventEmitter 对象的监听器的最大数量(默认为 10,但可以增加或减少):

door.setMaxListeners(50);

3 操作系统模块

为了能够真正的和操作系统交互,Node.js 同样提供了操作系统模块;该模块提供了许多函数,可用于从底层的操作系统和程序运行所在的计算机上检索信息并与其进行交互,引用方法保持一致:

const os = require('os');

有一些有用的属性可以告诉我们一些与处理文件有关的关键事项:

  • os.EOL 可给出行定界符序列:在 LinuxmacOS 上为 n,在 Windows 上为 rn
  • os.constants.signals 可告知所有与处理过程信号相关的常量,例如 SIGHUPSIGKILL
  • os.constants.errno 可设置用于错误报告的常量,例如 EADDRINUSEEOVERFLOW

可以在 http://nodejs.cn/api/os.html#os_signal_constants 上阅读所有的内容。

现在看一下 os 提供的主要方法:

3.1 os.arch()

返回标识底层架构的字符串,例如 armx64arm64

3.2 os.cpus()

返回关于系统上可用的 CPU 的信息。

例如:

3.3 os.endianness()

根据是使用大端序或小端序编译 Node.js,返回 BELE

3.4 os.freemem()

返回代表系统中可用内存的字节数:

3.5 os.homedir()

返回到当前用户的主目录的路径,例如:

3.6 os.hostname()

返回主机名:

3.7 os.loadavg()

返回操作系统对平均负载的计算,这仅在 LinuxmacOS 上返回有意义的值;Windows 因为对系统负载的计算方法不同,返回值为 0

例如:

3.8 os.networkInterfaces()

返回系统上可用的网络接口的详细信息,例如(因为是我真实的电脑,所以我把 mac 地址遮蔽了,见谅):

3.9 os.platform()

返回为 Node.js 编译的平台:

  • darwin
  • freebsd
  • linux
  • openbsd
  • win32
  • ...等

3.10 os.release()

返回标识操作系统版本号的字符串:

3.11 os.tmpdir()

返回指定的临时文件夹的路径:

3.12 os.totalmem()

返回表示系统中可用的总内存的字节数:

3.13 os.type()

标识操作系统:

  • Linux
  • macOS 上为 Darwin
  • Windows 上为 Windows_NT(这是因为非 NT 架构的 Windows 系统现在占有率太低了,比如 Win 95、98、Me,所以默认如此显示)

3.14 os.uptime()

返回自上次重新启动以来计算机持续运行的秒数:

3.15 os.userInfo()

当前登录用户的信息:

4 流

流是为 Node.js 应用程序提供动力的基本概念之一,这是一种以高效的方式处理读/写文件、网络通信、或任何类型的端到端的信息交换。

流不是 Node.js 特有的概念,它是几十年前在 Unix 操作系统中引入的,程序可以通过管道运算符(|)对流进行相互交互。

例如,在传统的方式中,当告诉程序读取文件时,这会将文件从头到尾读入内存,然后进行处理。

使用流,则可以逐个片段地读取并处理(而无需全部保存在内存中)。

Node.js 的 stream 模块 提供了构建所有流 API 的基础,所有的流都是 EventEmitter 的实例(这样讲就容易明白了吧)。

4.1 为什么使用流

相对于使用其他的数据处理方法,流基本上提供了两个主要优点:

  • 内存效率:无需加载大量的数据到内存中即可进行处理
  • 时间效率:当获得数据之后即可立即开始处理数据,这样所需的时间更少,而不必等到整个数据有效负载可用才开始

4.2 流的示例

一个典型的例子是从磁盘读取文件。

使用 Node.jsfs 模块,可以读取文件,并在与 HTTP 服务器建立新连接时通过 HTTP 提供文件:

const http = require('http');
const fs = require('fs');const server = http.createServer(function(req, res) {fs.readFile(__dirname + '/data.txt', (err, data) => {res.end(data);});
});
server.listen(3000);

readFile() 读取文件的全部内容,并在完成时调用回调函数;而回调中的 res.end(data) 会返回文件的内容给 HTTP 客户端。

如果文件很大,则该操作会花费较多的时间,这个时候我们可以使用流,如下例:

const http = require('http');
const fs = require('fs');const server = http.createServer((req, res) => {const stream = fs.createReadStream(__dirname + '/data.txt');stream.pipe(res);
})
server.listen(3000);

当要发送的数据块已获得时就立即开始将其流式传输到 HTTP 客户端,而不是等待直到文件被完全读取。

4.3 pipe()

上面的示例使用了 stream.pipe(res) 这行代码:在文件流上调用 pipe() 方法。

该代码的作用是获取来源流,并将其通过管道传输到目标流,在该示例中,文件流通过管道传输到 HTTP 响应。

pipe() 方法的返回值是目标流,它可以链接多个 pipe() 调用,从而非常方便,如下所示:

src.pipe(dest1).pipe(dest2);

此构造相对于:

src.pipe(dest1);
dest1.pipe(dest2);

要更容易理解和编写。

4.4 流驱动的 Node.js API

由于它的优点,许多 Node.js 核心模块提供了原生的流处理功能,最值得注意的有:

  • process.stdin 返回连接到 stdin 的流
  • process.stdout 返回连接到 stdout 的流
  • process.stderr 返回连接到 stderr 的流
  • fs.createReadStream() 创建文件的可读流
  • fs.createWriteStream() 创建到文件的可写流
  • net.connect() 启动基于流的连接
  • http.request() 返回 http.ClientRequest 类的实例,该实例是可写流
  • zlib.createGzip() 使用 gzip(压缩算法)将数据压缩到流中
  • zlib.createGunzip() 解压缩 gzip
  • zlib.createDeflate() 使用 deflate(压缩算法)将数据压缩到流中
  • zlib.createInflate() 解压缩 deflate

4.5 不同类型的流

流分为四类:

  • Readable:可以通过管道读取、但不能通过管道写入的流(可以接收数据,但不能向其发送数据);当推送数据到可读流中时,会对其进行缓冲,直到使用者开始读取数据为止
  • Writable:可以通过管道写入、但不能通过管道读取的流(可以发送数据,但不能从中接收数据)
  • Duplex:可以通过管道写入和读取的流,基本上相对于是可读流和可写流的组合
  • Transform:类似于双工流、但其输出是其输入的转换的转换流

4.6 如何创建可读流

从 stream 模块获取可读流,对其进行初始化并实现 readable._read() 方法。

首先创建流对象:

const Stream = require('stream');
const readableStream = new Stream.Readable();

然后实现 _read

readableStream._read = () => {};

也可以使用 read 选项实现 _read

const readableStream = new Stream.Readable({read() {}
});

现在,流已初始化,可以向其发送数据了:

readableStream.push('hi!');
readableStream.push('ho!');

4.7 如何创建可写流

若要创建可写流,需要继承基本的 Writable 对象,并实现其 _write() 方法。

首先创建流对象:

const Stream = require('stream');
const writableStream = new Stream.Writable();

然后实现 _write

writableStream._write = (chunk, encoding, next) => {console.log(chunk.toString());next();
};

现在,可以通过以下方式传输可读流:

process.stdin.pipe(writableStream);

4.8 如何从可读流中获取数据

使用可写流:

const Stream = require('stream');const readableStream = new Stream.Readable({read() {}
});
const writableStream = new Stream.Writable();writableStream._write = (chunk, encoding, next) => {console.log(chunk.toString());next();
};readableStream.pipe(writableStream);readableStream.push('hi!');
readableStream.push('ho!');

也可以使用 readable 事件直接获取可读流数据:

readableStream.on('readable', () => {console.log(readableStream.read());
});

4.9 如何发送数据到可写流

使用流的 write() 方法:

writableStream.write('hey!n');

4.10 使用信号通知已结束写入的可写流

使用 end() 方法:

const Stream = require('stream');const readableStream = new Stream.Readable({read() {}
});
const writableStream = new Stream.Writable();writableStream._write = (chunk, encoding, next) => {console.log(chunk.toString());next();
};readableStream.pipe(writableStream);readableStream.push('hi!');
readableStream.push('ho!');writableStream.end();


眼看着基础的 Node.js 快要结束了,其实如果不算第三方的模块,它自带的东西的确就是这么多,我们的重点还是要有模块化开发的思路和方法论,或者大量的练习,毕竟熟能生巧么。

监听js变量的变化_Node.js从零开始——事件、系统和流相关推荐

  1. 微信小程序 - 如何监听globalData 变量的变化

    最新发现 这个才是真正的解决办法啊! 一.app.js中定义watch函数 onLaunch() {// ... }, watch:function(method){var obj = this.gl ...

  2. js监听浏览器tab页面变化

    js监听浏览器tab页面变化 引言 今天写到一个需求,当用户离开当前页面的时候,需要关闭页面的一个功能,查找资料发现了这个指令,个人觉得很有用,记录下来... 直接上代码 mounted:(){thi ...

  3. js 监听div内容的变化

    使用DOMNodeInserted可以监听div内容的变化 $("#change").bind("DOMNodeInserted",function(){   ...

  4. js 监听dom属性的变化,如id,class

    tinymce中的颜色选择器,选择后只有下划线变了颜色,UI要求字体也变色.下划线和上面的字母都是svg中的path,没有change之类的事件可以监听. <editor@onInit=&quo ...

  5. watch监听vuex内部数据变化

    2019独角兽企业重金招聘Python工程师标准>>> //使用计算属性连接vuex的变量,在用watch去监听,变相的实现监听vuex内部state变化 <template& ...

  6. 监听页面高度变化_js监听屏幕的高度变化

    之前用VUE写完一个聊天界面,对于ios的效果该优化的地方都已经优化,且已上线.现在因项目需要,在另外一个angularjs+ionic的框架也要写一个聊天页面,目前还在开发中,发现忘记了一些很关键的 ...

  7. android 信号强度变化,Android监听WIFI网络的变化并且获得当前信号强度

    MainActivity如下: package cc.testwifi; import android.os.Bundle; import android.app.Activity; /** * De ...

  8. 监听mysql表内容变化 使用canal_2 监听mysql表内容变化,使用canal

    mysql本身是支持主从的(master slave),原理就是master产生的binlog日志记录了所有的增删改语句,将binlog发送到slave节点进行执行即可完成数据的同步. canal是阿 ...

  9. ios 监听数组个数的变化_iOS 监听数组的变化

    这里用的是KVO的方式来实现的, 首先有一个 testArray 这个数组需要监听里面的数据变化 NSMutableArray *testArray; 然后给这个数组注册监听 testArray = ...

最新文章

  1. 第二届清华大学iCenter量化策略挑战赛开幕!
  2. 2.2栈的另一个应用:括号匹配
  3. 面试项目亮点_怎样在面试中更好地介绍自己的项目经验?
  4. SAP云平台ABAP编程环境免费账号使用过程中的一些问题
  5. preg_match_all中的标记
  6. 关于批量导入数据以及调优的一些总结
  7. css实现垂直居中6种方法
  8. robocode 创建机器人
  9. 谷歌地图高精度模型提取3
  10. svn ankhsvn_AnkhSVN和Monad SVN提供商?
  11. 小学语文历史重点名人以及解析大全
  12. 迈开职场充电第一步,让我们在这个冬天邂逅社科院杜兰金融管理硕士项目
  13. 《精益创业》- 天下大事必作于细,天下难事必作于易
  14. 最快Android模拟器Genymotion的安装与使用完整教程(多图,慎点)
  15. [攻防世界]crypto新手练习区Caesar
  16. 树莓派安装mplayer,并使用命令查看摄像头
  17. 详述 Spring MVC 启动流程及相关源码分析
  18. python绘制人际关系图_干货!利用Python绘制精美网络关系图
  19. SeekBar进度条滑动调节屏幕亮度
  20. 系统架构设计 2.1 管道-过滤器风格

热门文章

  1. React.js 2016 最佳实践 徬梓阅读 1584收藏 71
  2. node js 部署相关
  3. XenServer 6.5实战系列之三:Prepare for XenServer 6.5
  4. delphi xe6 android ListView增加 Header或Footer 的方法
  5. S5PV210 FirstAndroidAPP] ERROR: Application requires API version 版本不对的问题
  6. 使用动画播放文件夹中的图片
  7. 在数据库‘master’中拒绝CREATE DATABASE权限 的问题
  8. Programming C# 4th Edition 中文版/英文版 对照阅读体验
  9. python中count()方法
  10. php 可以动态的new一个变量类名