Nodejs 模块 (三)

核心模块

  • node自带的模块

util

  • util util.inherits
  • util.promisify

1)promisify

  • ncp 拷贝的一个模块
  • 先初始化环境 npm init
  • 安装ncp yarn add ncp
1
2
3
4
5
6
let ncp = require('ncp'); // 第三方库
let path = require('path');
// 拷贝文件
ncp(path.resolve(__dirname,"note.md"), path.resolve(__dirname,"note1.md"), err=>{
console.log(err)
})

现在希望能转化成下面这种形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(async()=>{
await ncp(path.resolve(__dirname,"note.md"), path.resolve(__dirname,"note1.md"))
console.log('拷贝成功');
})();

// 引入util模块
let ncp = require('ncp'); // 第三方库
let path = require('path');
let util = require('util');
ncp = util.promisify(ncp);
(async()=>{
await ncp(path.resolve(__dirname,"note.md"), path.resolve(__dirname,"note2.md"))
console.log('拷贝成功');
})();

手写原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let ncp = require('ncp'); // 第三方库
let path = require('path');
let util = require('util');

const promisify = (fn) => (...args) => {
return new Promise( (resolve, reject) => {
fn(...args, err=>{ // 只针对node,因为只有node才是这种error-first的形式
if(err) reject(err);
resolve();
});
})
}

ncp = promisify(ncp);
(async()=>{
await ncp(path.resolve(__dirname,"note.md"), path.resolve(__dirname,"note3.md"))
console.log('拷贝成功');
})();

2) inherits

node内部不是用es6写的 好多方法都是用es5来写的, 实现类的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function  Parent() {

}
function Child() {

}
// 只继承公共方法
Child.prototype.__proro__ = Parent.prototype;
Reflect.setPrototypeOf(Child.prototype, Parent.prototype);
Child.prototype = Object.create(Parent.prototype);

let {inherits} = require('util');
inherits(Child, Parent); // 这行和line10、11是等价的 继承公共属性

// 可以显示隐藏属性
console.log(util.inspect(Array.prototype,{showHidden:true}));
console.log(util.isPrimitive);

events

  • 事件触发器
发布订阅模块
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let EventEmitter = require('events');
// on emit
let e = new EventEmitter();
let listener1 = ()=>{
console.log('哭');
}
let listener2 = ()=>{
console.log('逛街');
}
e.on("女生失恋", listener1);
e.on("女生失恋", listener2);
// 发布
e.emit("女生失恋");
// 哭 逛街
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 1.events.js
let EventEmitter = require('events');
let util = require('util')
function Girl(){ // 自己写个类
}
util.inherits(Girl,EventEmitter);
let girl = new Girl();
// on emit
let listener1 = ()=>{
console.log('哭');
}
let listener2 = ()=>{
console.log('逛街');
}
girl.on("女生失恋", listener1);
girl.on("女生失恋", listener2);
// 发布
girl.emit("女生失恋");
//哭 逛街
手写events模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// events.js
function EventEmitter(){
this._events = Object.create(null);
}
// {'name': [listener, listener]}
EventEmitter.prototype.on = function (eventName, callback) {
// 不管任何人调动了on方法 都可以增加_events
if( !this._events) this._events = Object.create(null);
if(this._events[eventName]){
this._events[eventName].push(callback);
}else{
this._events[eventName] = [callback];
}
}
EventEmitter.prototype.emit = function(eventName, ...args){
if(this._events[eventName]){
this._events[eventName].forEach(fn => {
fn(...args);
});
}
}
module.exports = EventEmitter;
// 上面1.events.js中引入的是 require('./events');

Tips

  • Object.create(null);   这样创建的对象是没有属性的 可以直接在浏览器打印
  • {} 这样创建的对象有一堆属性
newListener
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//let EventEmitter = require('./events');
let EventEmitter = require('events');
let util = require('util')
function Girl(){ // 自己写个类
}
util.inherits(Girl,EventEmitter);
let girl = new Girl();

girl.on('newListener', (type)=>{ // 这个可以监听到用户做了哪些监听
console.log(type);
})

let listener1 = (w)=>{
console.log('哭', w);
}
let listener2 = (w)=>{
console.log('逛街', w);
}
girl.on("女生失恋", listener1);
girl.on("女生失恋", listener2);
// 发布
girl.emit("女生失恋", '我');
/**打印结果
女生失恋
女生失恋
哭 我
逛街 我
*/
比如说现在想要监听失恋这个事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let EventEmitter = require('events');
let util = require('util')
function Girl(){
}
util.inherits(Girl,EventEmitter);
let girl = new Girl();

girl.on('newListener', (type)=>{ // 这个可以监听到用户做了哪些监听
if(type === '失恋'){
girl.emit(type);
}
})
girl.on('失恋', function(){
console.log('监听到了 执行');
})
// 这里没有监听到 因为先走的line8-12,line13-15还没有放到数组里, 执行顺序的问题
解决办法如下 nextTick
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let EventEmitter = require('events');
let util = require('util')
function Girl(){
}
util.inherits(Girl,EventEmitter);
let girl = new Girl();

girl.on('newListener', (type)=>{
if(type === '失恋'){
process.nextTick(()=>{ // 这里会等到同步代码 line15-17执行完之后再执行
girl.emit(type);
})
}
})
girl.on('失恋', function(){
console.log('监听到了 执行');
})
// 监听到了 执行
如果绑定2个失恋事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
let EventEmitter = require('events');
let util = require('util')
function Girl(){ // 自己写个类
}
util.inherits(Girl,EventEmitter);
let girl = new Girl();

girl.on('newListener', (type)=>{
console.log("1~");
if(type === '失恋'){
console.log("2~");
process.nextTick(()=>{
console.log("3~");
girl.emit(type);
})
}
})
girl.on('失恋', function(){
console.log('监听到了 执行');
})
girl.on('失恋', function(){
console.log('监听到了 执行');
})
/*执行结果:
1~
2~
1~
2~
3~
监听到了 执行
监听到了 执行
3~
监听到了 执行
监听到了 执行
*/

分析: line18绑定到数组中{‘失恋’:[fn]},绑定一次执行一次line8,line21绑定到数组中{‘失恋’: [fn,fn]},on绑定一次,line8执行一次,因为line12是异步的,同步执行完之后line12执行,刚才line8执行了2次(线程里存储了2个微任务),所以line12这里也执行2次,这样一共就四次了。

once 解决
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let EventEmitter = require('events');
let util = require('util')
function Girl(){
}
util.inherits(Girl,EventEmitter);
let girl = new Girl();

girl.on('newListener', (type)=>{
if(type === '失恋'){
process.nextTick(()=>{
girl.emit(type);
})
}
})
girl.once('失恋', function(){
console.log('监听到了 执行');
})
girl.once('失恋', function(){
console.log('监听到了 执行');
})
/*执行结果
监听到了 执行
监听到了 执行
*/
off 解除绑定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let EventEmitter = require('./events');
let util = require('util')
function Girl(){
}
util.inherits(Girl,EventEmitter);
let girl = new Girl();

const fn1 = function(){
console.log('监听到了 执行');
}
girl.once('失恋', fn1);
girl.off('失恋', fn1);
girl.once('失恋', function(){
console.log('监听到了 执行');
})
girl.emit("失恋");
/*执行结果:
监听到了 执行
*/
手写events 优化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
function EventEmitter(){
this._events = Object.create(null);
}
// {'name': [listener, listener]}
EventEmitter.prototype.on = function (eventName, callback) {
// 不管任何人调动了on方法 都可以增加_events
if( !this._events) this._events = Object.create(null);
// 监听绑定的事件 不是newLister就调用newListener
if(eventName !== 'newListener'){
this.emit('newListener', eventName);
}
// 当这里调用的事件不是newLister,是失恋,那就把'失恋'传到girl.on('newListener', fn(type))后面的那个方法里,type就是'失恋',每次on事件,newListener就能监听到,存起来,然后执行

if(this._events[eventName]){
this._events[eventName].push(callback);
}else{
this._events[eventName] = [callback];
}
}
EventEmitter.prototype.once = function(eventName, callback){
// 绑定 => 执行后 => 再删除
let one = ()=>{ // 2) 会触发one函数
callback(); // 触发原有的逻辑
// 删除自己
this.off(eventName, one); // 再将one删除掉
}
one.l = callback; // 用自定义属性 保存原有的绑定函数
this.on(eventName, one); // 1) 先绑定
}
EventEmitter.prototype.off = function(eventName, callback) {
if(this._events[eventName]){
this._events[eventName] = this._events[eventName].filter( fn=>{
return fn != callback && fn.l !== callback;
})
}
}
EventEmitter.prototype.emit = function(eventName, ...args){
if(this._events[eventName]){
this._events[eventName].forEach(fn => {
fn(...args);
});
}
}
module.exports = EventEmitter;

node事件绑定 核心模块
on 、emit 、once 、newListener 、off