本文已参与「新人创作礼」活动,一起开启掘金创作之路。
一.Node.js
1.事件驱动程序设计是什么?
事件驱动程序设计(Event-Driven Programming)是一种计算机程序设计模型。这种模型的程序运行流程是由用户的操作(如鼠标的按键、键盘的按键操作)或者是由其他程序的消息来驱动的。 事件驱动的程序至少会有一个事件队列,当有新的请求进来时会被插入到队列中,然后通过循环来检测队列中的事件,当发现有一个事件发生时就会调用回调函数。 Node.js的JavaScript(JS)线程是单线程运行的,通过一个事件循环(Event Loop)来循环取出消息队列(Event Queue)中的消息进行处理,处理过程基本上就是去调用该消息对应的回调函数。
2.非阻塞I/O是什么?
I/O模型的一种,与之相对的还有阻塞I/O。 简单来说,阻塞I/O就是进行I/O操作的时候,进程会被阻塞,直到I/O操作完成后进程才会解除阻塞状态继续执行。而非阻塞I/O在进行I/O操作时,进程不会被阻塞,进程继续执行;如果需要知道I/O操作的结果,可以通过轮询的方式。例如,可以一边读取文件,一边执行其他命令,在文件读取完成后,将文件内容作为回调函数的参数返回。这样在执行代码时就没有阻塞或等待文件 I/O 操作。这就大大提高了 Node.js 的性能,可以处理大量的并发请求。回调函数一般作为函数的最后一个参数出现:
function foo1(name, age, callback) { }
function foo2(value, callback1, callback2) { }
注:阻塞是按顺序执行的,而非阻塞是不需要按顺序的,所以如果需要处理回调函数的参数,就需要写在回调函数内。
3.EventEmitter类:
Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。Node.js 里面的许多对象都会分发事件:一个 net.Server 对象会在每次有新连接时触发一个事件, 一个 fs.readStream 对象会在文件被打开的时候触发一个事件。 所有这些产生事件的对象都是events.EventEmitter 的实例。
而events模块只提供了一个对象:events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。EventEmitter 对象如果在实例化时发生错误,会触发 error 事件。当添加新的监听器时,newListener 事件会触发,当监听器被移除时,removeListener 事件被触发。
引入模块技巧:
当库比较大,只需要引用部分子模块的时候,可以将
var events = require('events');
var xxx = new events.EventEmitter();
省略的写为
var xxx = require('events').EventEmitter;
EventEmitter 的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter 支持 若干个事件监听器。
当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。
常用的一些eventemitter的方法:
-
addListener(event, listener) 为指定事件添加一个监听器到监听器数组的尾部。
-
on(event, listener) 为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。
-
once(event, listener) 为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。
-
removeListener(event, listener) 移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。它接受两个参数,第一个是事件名称,第二个是回调函数名称。
-
removeAllListeners([event]) 移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。
-
setMaxListeners(n) 默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于改变监听器的默认限制的数量。
-
listeners(event) 返回指定事件的监听器数组。
-
emit(event, [arg1], [arg2], [...]) 按监听器的顺序执行执行每个监听器,如果事件有注册监听返回 true,否则返回 false。
类方法:
listenerCount(emitter, event) 返回指定事件的监听器数量。 实例:
events.emitter.listenerCount(eventName)
继承 EventEmitter
大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。
为什么要这样做呢?原因有两点:
首先,具有某个实体功能的对象实现事件符合语义, 事件的监听和发生应该是一个对象的方法。
其次 JavaScript 的对象机制是基于原型的,支持 部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。
4.Buffer(缓冲区)
buffer是什么?
因为js语言本身只有字符串数据类型,没有二进制数据类型,但在处理TCP流或者文件流的时候,必须用到二进制数据。所以定义了Buffer这个类,用来创建一个专门存放二进制数据的缓存区。
注:在v6.0之前创建Buffer对象直接使用new Buffer()构造函数来创建对象实例,但是Buffer对内存的权限操作相比很大,可以直接捕获一些敏感信息,所以在v6.0以后,官方文档里面建议使用 Buffer.from() 接口去创建Buffer对象。
创建Buffer类:
Buffer 提供了以下 API 来创建 Buffer 类:
- Buffer.alloc(size[, fill[, encoding]]): 返回一个指定大小的 Buffer 实例,如果没有设置 fill,则默认填满 0
- Buffer.allocUnsafe(size): 返回一个指定大小的 Buffer 实例,但是它不会被初始化,所以它可能包含敏感的数据
- Buffer.allocUnsafeSlow(size)
- Buffer.from(array): 返回一个被 array 的值初始化的新的 Buffer 实例(传入的 array 的元素只能是数字,不然就会自动被 0 覆盖)
- Buffer.from(arrayBuffer[, byteOffset[, length]]): 返回一个新建的与给定的 ArrayBuffer 共享同一内存的 Buffer。
- Buffer.from(buffer): 复制传入的 Buffer 实例的数据,并返回一个新的 Buffer 实例
- Buffer.from(string[, encoding]): 返回一个被 string 的值初始化的新的 Buffer 实例
写入缓存区:
语法
写入 Node 缓冲区的语法如下所示:
buf.write(string[, offset[, length]][, encoding])
参数
参数描述如下:
- string - 写入缓冲区的字符串。
- offset - 缓冲区开始写入的索引值,默认为 0 。
- length - 写入的字节数,默认为 buffer.length
- encoding - 使用的编码。默认为 'utf8' 。
根据 encoding 的字符编码写入 string 到 buf 中的 offset 位置。 length 参数是写入的字节数。 如果 buf 没有足够的空间保存整个字符串,则只会写入 string 的一部分。 只部分解码的字符不会被写入。
返回值
返回实际写入的大小。如果 buffer 空间不足, 则只会写入部分字符串。
从缓冲区读取数据
语法
读取 Node 缓冲区数据的语法如下所示:
buf.toString([encoding[, start[, end]]])
参数
参数描述如下:
- encoding - 使用的编码。默认为 'utf8' 。
- start - 指定开始读取的索引位置,默认为 0。
- end - 结束位置,默认为缓冲区的末尾。
返回值
解码缓冲区数据并使用指定的编码返回字符串。