正文
核心模块是Node.js的心脏,主要是有一些精简高效的库组成(这方面和Python有很大的相似之处),为Node.js提供了基础的API。主要内容包括:
Node.js核心入门(一)
全局对象
常用工具
事件机制
Node.js核心入门(二)
文件系统访问
HTTP服务器与客户端
全局对象
全局对象我想学过JavaScript的都知道在浏览器是window,在程序的任何地方都可以访问到全局对象,而在Node.js中,这个全局对象换成了global,所有的全局变量(除了global本身)都是global对象的属性。而我们在Node.js中能够直接访问的对象通常都是global的属性,如:console,process等。
全局对象与全局变量
global最根本的作用就是作为全局变量的宿主。按照ECMAScript规范,满足以下条件的变量即为全局变量:
在最外层定义的变量(在Node.js中不存在,因为Node.js的代码在模块中执行,不存在在最外层定义变量)
全局对象的属性
隐式定义的变量(即未定义而直接进行赋值的变量)
当我们定义一个全局变量的时候,这个全局变量会自动成为全局变量的属性。
process
process 对象是一个全局变量,它提供当前 Node.js 进程的相关信息,以及控制当前 Node.js 进程。通常我们在写本地命令行程序的时候,少不了和它打交道。下面是它的最常用的成员方法:
1.process.argv
process.argv 属性返回一个数组,这个数组包含了启动Node.js进程时的命令行参数。第一个元素为process.execPath,第二个元素为当前执行的JavaScript文件路径,剩余的元素为其他命令行参数。
例如存储一个名为argv.js的文件:
// print process.argv
process.argv.forEach((val, index) => {
console.log(`${index}: ${val}`);
});
复制代码
则命令行运行时这样的:
$ node process-args.js one two=three four
0: /usr/local/bin/node
1: /Users/mjr/work/node/process-args.js
2: one
3: two=three
4: four
复制代码
2.process.stdout
process.stdout是标准输出流,通常我们使用的console.log()输出打印字符,而process.stdout.write()函数提供了更为底层的接口。
process.stdout.write('请输入num1的值:');
复制代码
3.process.stdin
process.stdin是标准输入流,初始时它是暂停的,要想从标准输入读取数据,我们必须恢复流,并手动编写流的事件响应函数。
/*1:声明变量*/
var num1, num2;
/*2:向屏幕输出,提示信息,要求输入num1*/
process.stdout.write('请输入num1的值:');
/*3:监听用户的输入*/
process.stdin.on('data', function (chunk) {
if (!num1) {
num1 = Number(chunk);
/*4:向屏幕输出,提示信息,要求输入num2*/
process.stdout.write('请输入num2的值');
} else {
num2 = Number(chunk);
process.stdout.write('结果是:' + (num1 + num2));
}
});
复制代码
4.process.nextTick(callback[, ...args])
...args 调用 callback时传递给它的额外参数 process.nextTick()方法将 callback 添加到"next tick 队列"。一旦当前事件轮询队列的任务全部完成,在next tick队列中的所有callbacks会被依次调用。这种方式不是setTimeout(fn, 0)的别名。它更加有效率,因此别用setTimeout去代替process.nextTick。事件轮询随后的ticks 调用,会在任何I/O事件(包括定时器)之前运行。
console.log('start');
process.nextTick(() => {
console.log('nextTick callback');
});
console.log('scheduled');
// start
// scheduled
// nextTick callback
复制代码
console
console 模块提供了一个简单的调试控制台,类似于 Web 浏览器提供的 JavaScript 控制台。该模块导出了两个特定的组件:
一个 Console 类,包含 console.log() 、 console.error() 和 console.warn() 等方法,可以被用于写入到任何 Node.js 流。
一个全局的 console 实例,可被用于写入到 process.stdout 和 process.stderr。全局的 console 使用时无需调用 require('console')。(注意:全局的 console 对象的方法既不总是同步的(如浏览器中类似的 API),也不总是异步的(如其他 Node.js 流)。
比如全局下的常见的console实例:
console.log('hello,world');
// hello,world
console.log('hello%s', 'world');
// helloworld
console.error(new Error('错误信息'));
// Error: 错误信息
const name = '描述';
console.warn(`警告${name}`);
// 警告描述
console.trace() // 向标准错误流输出当前的调用栈
复制代码
常见的Console类:
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello,world');
// hello,world
myConsole.log('hello%s', 'world');
// helloworld
myConsole.error(new Error('错误信息'));
// Error: 错误信息
const name = '描述';
myConsole.warn(`警告${name}`);
//警告描述
复制代码
常用工具 util
util 模块主要用于支持 Node.js 内部 API 的需求。大部分实用工具也可用于应用程序与模块开发者,用于弥补核心JavaScript的功能的不足。它的可以这样调用:
const util = require('util');
复制代码
1.util.inspect(object[, options])
util.inspect() 方法返回 object 的字符串表示,主要用于调试和错误输出。附加的 options 可用于改变格式化字符串的某些方面。它至少接受一个参数objet,即要转换的参数,而option则是可选的,可选内容如下:
showHidden 如果为 true,则 object 的不可枚举的符号与属性也会被包括在格式化后的结果中。默认为 false。
depth 指定格式化 object 时递归的次数。这对查看大型复杂对象很有用。默认为 2。若要无限地递归则传入 null。
colors 如果为 true,则输出样式使用 ANSI 颜色代码。默认为 false,可自定义。
customInspect 如果为 false,则 object 上自定义的 inspect(depth, opts) 函数不会被调用。默认为 true。
showProxy 如果为 true,则 Proxy 对象的对象和函数会展示它们的 target 和 handler 对象。默认为 false。
maxArrayLength 指定格式化时数组和 TypedArray 元素能包含的最大数量。默认为 100。设为 null 则显式全部数组元素。设为 0 或负数则不显式数组元素。
breakLength 一个对象的键被拆分成多行的长度。设为 Infinity 则格式化一个对象为单行。默认为 60。
例如,查看 util 对象的所有属性:
const util = require('util');
console.log(util.inspect(util, { showHidden: true, depth: null }));
复制代码
2.util.callbackify(original)
util.callbackify(original)方法将 async 异步函数(或者一个返回值为 Promise 的函数)转换成遵循 Node.js 回调风格的函数。在回调函数中, 第一个参数 err 为 Promise rejected 的原因 (如果 Promise 状态为 resolved , err为 null ),第二个参数则是 Promise 状态为 resolved 时的返回值。例如:
const util = require('util');
async function fn() {
return await Promise.resolve('hello world');
}
const callbackFunction = util.callbackify(fn);
callbackFunction((err, ret) => {
if (err) throw err;
console.log(ret);
});
// hello world
复制代码
注意:
回调函数是异步执行的, 并且有异常堆栈错误追踪. 如果回调函数抛出一个异常, 进程会触发一个 'uncaughtException' 异常, 如果没有被捕获, 进程将会退出。
null 在回调函数中作为一个参数有其特殊的意义, 如果回调函数的首个参数为 Promise rejected 的原因且带有返回值, 且值可以转换成布尔值 false, 这个值会被封装在 Error 对象里, 可以通过属性 reason 获取。
function fn() {
return Promise.reject(null);
}
const callbackFunction = util.callbackify(fn);
callbackFunction((err, ret) => {
// 当Promise的rejecte是null时,它的Error与原始值都会被存储在'reason'中。
err && err.hasOwnProperty('reason') && err.reason === null; // true
});
复制代码
事件驱动 events
events是Node.js最重要的模块,原因是Node.js本身架构就是事件式的,大多数 Node.js 核心 API 都采用惯用的异步事件驱动架构,而它提供了唯一的接口,因此堪称Node.js事件编程的及时。events 模块不仅用于用户代码与 Node.js 下层事件循环的交互,还几乎被所有的模块依赖。
所有能触发事件的对象都是 EventEmitter 类的实例。这些对象开放了一个 eventEmitter.on() 函数,允许将一个或多个函数绑定到会被对象触发的命名事件上。事件名称通常是驼峰式的字符串,但也可以使用任何有效的 JavaScript 属性名。对于每个事件, EventEmitter支持 若干个事件监听器。当事件发射时,注册到这个事件的事件监听器被依次调用,事件参数作 为回调函数参数传递。
例如:
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
// eventEmitter.on() 方法用于注册监听器
myEmitter.on('event', () => {
console.log('触发了一个事件!');
});
// eventEmitter.emit() 方法用于触发事件
myEmitter.emit('event');
复制代码
下面让我们来看看EventEmitter最常用的API:
EventEmitter.on(event, listener) 方法可以添加 listener 函数到名为 eventName 的事件的监听器数组的末尾。不会检查 listener 是否已被添加。多次调用并传入相同的 eventName 和 listener 会导致 listener 被添加与调用多次。
emitter.prependListener(eventName, listener)方法可以添加 listener 函数到名为 eventName 的事件的监听器数组的开头。不会检查 listener 是否已被添加。多次调用并传入相同的 eventName 和 listener 会导致 listener 被添加与调用多次。
eventEmitter.emit() 方法允许将任意参数传给监听器函数。当一个普通的监听器函数被 EventEmitter 调用时,标准的 this 关键词会被设置指向监听器所附加的 EventEmitter。
// 实例:
const myEE = new EventEmitter();
myEE.on('foo', () => console.log('a'));
myEE.prependListener('foo', () => console.log('b'));
myEE.emit('foo');
// 打印:
// b
// a
复制代码
EventEmitter.once(event, listener) 为指定事件注册一个单次监听器,即监听器最多只会触发一次,触发后立刻解除该监听器。
server.once('connection', (stream) => {
console.log('首次调用!');
});
复制代码
EventEmitter.removeListener(event, listener) 移除指定事件的某个监听器, listener 必须是该事件已经注册过的监听器。(注意:removeListener 最多只会从监听器数组里移除一个监听器实例。如果任何单一的监听器被多次添加到指定 eventName 的监听器数组中,则必须多次调用 removeListener 才能移除每个实例。)
const callback = (stream) => {
console.log('有连接!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
复制代码
EventEmitter.removeAllListeners([event]) 移除所有事件的所有监听器,如果指定 event ,则移除指定事件的所有监听器
const callback = (stream) => {
console.log('有连接!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
复制代码
error 事件
EventEmitter 定义了一个特殊的事件 error ,它包含了“错误”的语义,我们在遇到异常的时候通常会发射 error 事件。当 error被发射时,EventEmitter规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并打印调用栈。我们一般要为会发射 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。
var events = require('events');
var emitter = new events.EventEmitter();
emitter.emit('error');
复制代码
继承EventEmitter
大多数情况下,我们不会直接使用EventEmitter,而是在对象中继承它,包括fs,http在内的只要是支持事件响应的核心模块都是EventEmitter的子类。这样做的原因有以下两个:
具有某个实体功能的对象实现事件符合语义,事件的监听和发射应该是一个对象的方法。
JavaScript 的对象机制是基于原型的,支持部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。
来源:https://juejin.cn/post/6844903586283913230