上章讲了学习node,应该去学习什么,对这些框架去进行学习现在咋们聊聊如何用原生来进行操作

主要来讲一下events-事件触发器

events是什么东西呢?他的英文翻译,活动,事件的意思,在计算机语言离也是事件的意思,什么是事件呢?最简单的,鼠标移上是事件吧,点击是吧,些都是事件的一种,这些都是常用的一种事件,现在来讲讲node里的事件

 node.js中事件的发射器 ,也就是调取的东西在这

class EventEmitter {
// 返回正在监听名为 eventName的事件的监听器数量
static listenerCount(emitter: EventEmitter, type: string | number): number; // 每个事件 默认可注册监听器的最大数量
static defaultMaxListeners: number; // 修改事件 监听器数量的限制
setMaxListeners(n: number): this; // 调用指定名称事件的 所有监听器
emit(type: string | number, ...args: any[]): boolean; // 添加listener 函数到名为 type 的事件监听器数组末尾
addListener(type: string | number, listener: Listener): this;
// addListener 方法的别名
on(type: string | number, listener: Listener): this; // 添加一个单次listener 函数到名为 eventName的事件。下次触发eventName事件时,监听器会被移除,然后调用
once(type: string | number, listener: Listener): this; // 从type 的事件监听器数组中移除指定的 listener
removeListener(type: string | number, listener: Listener): this; // 移除全部或指定type的监听器
removeAllListeners(type?: string | number): this; // 返回type事件下的监听器数组
listeners(type: string | number): Listener[]; // 返回type事件下监听器数量
listenerCount(type: string | number): number;
}

先来讲一个简单的实例

EventEmitter的实例,绑定一个监听器。用eventEmitter.on()的方法来进行注册一个监听器,eventEmitter.emit()方法来触发引用事件。

//引入events模块
const EventEmitter = require('events'); //创建一个新实例
const myEmitter = new EventEmitter();
//创建一个监听
myEmitter.on('发工资了', () => {
console.log('钱没了');
});
//触发监听事件
myEmitter.emit('发工资了');

eventEmitter.on()与eventEmitter.once()的区别,最明显的区别就是,他们后面的拼写不一样,着实在的的,其实是他们的运行,其中一个能够只要进行触发就会一直运行下去,触发一次运行一次,还有一个是不管你触发多少次,他只运行第一次。

const myEmitter = new MyEmitter();
let m = 0;
myEmitter.once('event', () => {
console.log(++m);
});
myEmitter.emit('event');
// 打印: 1
myEmitter.emit('event');
// 不触发

上面说了on和once的区别,现在又来了一个addListener,同样是添加监听事件的东西来看下他的使用方法

emitter.addListeners("有钱", ()=>{
console.log("买车") })
emitter.emit('有钱');
//1111

  可能细心的人发现了,这个方法和上面的又有什么分别呢,我能告诉,没有分别,就是拼的不一样罢了,来验证一下

console.log(emitter.on == emitter.addListener)
//true

eventEmitter.on()之外还有另外的一种就是eventEmitter.once()的方法,其实这两个东西的区别用上面的代码看还真的没有什么多大的区别,但把他们拆分出底层来真的区别挺大的,完全就是两个东西来看下拆分区别

  区别

addListener监听事件

// 这里我们模拟 EventEmitter 类的定义实现了一个简单的具有添加事件监听器和触发事件的类
class MyEmitter {
// 首先初始化对象的时候创建了一个事件的容器
constructor () {
this.eventsPool = {}
} // 添加事件监听器方法 将监听器添加到指定类型下的数组中
addListener (type, listener) {
if (!this.eventsPool) {
this.eventsPool = {}
}
if(!this.eventsPool[type]){
this.eventsPool[type]=[]
}
this.eventsPool[type].push(listener)
} // 触发事件方法 遍历类型下所有事件监听器并调用
emit (type, ...args) {
let listeners = this.eventsPool[type]
if (listeners) {
for (let listener of listeners) {
listener(args)
}
}
}
}
myEmitter.addListener('once', function() {
console.log('once')
})
myEmitter.emit('once')

  

 once事件 

class MyEmitter {
// 首先初始化对象的时候创建了一个事件的容器
constructor () {
this.eventsPool = {}
} // 添加事件监听器方法 将监听器添加到指定类型下的数组中
once (type, listener) {
if (!this.eventsPool) {
this.eventsPool = {}
}
if(!this.eventsPool[type]){
this.eventsPool[type]=[]
}
// >>> once 对listener 进行改造
const onceListener = (...args) => {
let listeners = this.eventsPool[type]
let lIndex = listeners.findIndex(listener => listener === onceListener)
if (lIndex !== -1) {
listeners.splice(lIndex, 1)
}
console.log("asfasf")
listener.apply(this, args)
} // >>> // >>> 将改造好的listener 放入监听器数组
this.eventsPool[type].push(onceListener)
// console.log(this.eventsPool[type])
} // 触发事件方法 遍历类型下所有事件监听器并调用
emit (type, ...args) {
let listeners = this.eventsPool[type]
if (listeners) {
for (let listener of listeners) {
listener(args)
}
}
}
} const myEmitter = new MyEmitter()
myEmitter.once('once', function() {
console.log('once11111')
})
myEmitter.emit('once')

  

  

EventEmitter 会按照监听器注册的顺序同步地调用所有监听器。 所以必须确保事件的排序正确,且避免竞态条件。 可以使用 setImmediate() 或 process.nextTick() 切换到异步模式:(规范,第一个值是错误,第二个是值)区别→→

const myEmitter = new MyEmitter();
myEmitter.on('event', (a, b) => {
setImmediate(() => {
console.log('异步进行');
});
});
myEmitter.emit('event', 'a', 'b');

 开始的时候我以为他的竞态事件是他内部的竞争关系,也就是下面这样,其实这种理解是有误的

const myEmitter = new MyEmitter();
myEmitter.on('event', (a, b) => {
console.log(111)
setImmediate(() => {
console.log('异步进行');
});
console.log(222)
});
myEmitter.emit('event', 'a', 'b'); //111
//222
//异步进行

  他这里避免竞态关系是避免监听事件的竞态关系

const myEmitter = new MyEmitter();
myEmitter.on('event', (a, b) => {
console.log(111)
});
myEmitter.on('event', (a, b) => {
setImmediate(() => {
console.log('异步进行');
});
});myEmitter.on('event', (a, b) => {
console.log(222)
});
myEmitter.emit('event', 'a', 'b'); //111
//222
//异步进行

  虽然输出的结果是一样的,但是他这个避免竞态关系,确确实实是给监听事件的,而不是给输出事件的

 

 

  其实node的实例,在每个方法下都有限制要注册多少个监听器的,可以用

EventEmitter.defaultMaxListeners来查看下,还可以通过更改他的数值来限定用多少个监听器。
console.log(EventEmitter.defaultMaxListeners)
//默认值10

  侦听器太多,可能导致内存泄漏,所以存在这样一个警告

// 设置事件最大监听个数
girl.setMaxListeners(2);
girl.on("失恋", () => console.log("哭了"));
girl.on("失恋", () => console.log("喝酒"));
girl.on("失恋", () => console.log("吸烟"));
girl.emit("失恋");
// MaxListenersExceededWarning: 3 失恋 listeners added
// 哭了
// 喝酒
// 吸烟

  

  

  现在添加监听的事件有了,来看看删除监听事件

const EventEmitter = require('events');
const myEE = new EventEmitter();
const sss = () =>{
console.log("aaa")
}
myEE.on('foo', sss);
myEE.on('bar', () => {}); myEE.removeListener('foo', sss) console.log(myEE.listeners('foo'))
//[]

  上面的例子就是删除掉了foo监听里的sss事件,看上面了例子发现一个不认识的方法,那就是console.log内的方法myEE.listeners(name)这又是啥东西,为啥打印他就能知道他方法内有没有删除呢

这个东西吧,其实就是node.js里面给的一种方法,他返回的是事件监听器里的副本,也就是相当于方法之类的。和他对应的还有一个那就是myEE.listenerCount(name) 他返回的就是这个事件中有多少的监听数量。

const EventEmitter = require('events');
const myEE = new EventEmitter();
myEE.on('bar', () => {});
myEE.on('bar', () => {});
myEE.on('bar', () => {});
myEE.listenerCount('bar')
console.log(myEE.listenerCount('bar'))
//3
myEE.listeners('foo')
console.log(myEE.listeners('bar'))
//[ [Function], [Function], [Function] ]

  

如过他要是多个不重名的方法应该怎么办呢,要怎么把他查出来,那就要用到这个方法了myEE.eventNames()    这个方法他能够直接的反应你代码用都有那些方法。

const EventEmitter = require('events');
const myEE = new EventEmitter();
const sss = () =>{
console.log("aaa")
}
myEE.on('foo', sss);
myEE.on('bar', () => {});
console.log(myEE.eventNames());
//[ 'foo', 'bar' ]

  

扯的有点远了,回到刚才,移除监听事件上面的移除事件,是根据条件来进行移除某一条,某一个监听的,下面我们来把所有的监听全部干掉。

const EventEmitter = require('events');
const myEE = new EventEmitter();
const sss = () =>{
console.log("aaa")
}
myEE.on('foo', sss);
myEE.on('bar', () => {}); myEE.removeAllListeners()

  这样你不管console.log哪个监听他都是空的

使用prependListener添加事件并执行
var EventEmitter = require("events")
const girl = new EventEmitter();
girl.on("失恋", () => console.log("哭了"));
girl.prependListener("失恋", () => console.log("喝酒")); girl.emit("失恋");

  


这里还有一个比较有意思的,看下案例
const myEmitter = new MyEmitter();

const callbackA = () => {
console.log('A');
myEmitter.removeListener('event', callbackB);
}; const callbackB = () => {
console.log('B');
}; myEmitter.on('event', callbackA); myEmitter.on('event', callbackB); myEmitter.emit('event');
//???? myEmitter.emit('event');
//???
// callbackA 移除了监听器 callbackB,但它依然会被调用。
// 触发时内部的监听器数组为 [callbackA, callbackB]
 

EventEmitter 总结

events 模块在 NodeJS 中的使用率非常高,很多其他模块的事件执行机制都是通过继承该模块的 EventEmitter 类来实现的,比如 ReadStream(可读流)、WriteStream(可写流)、net(tcp)和 http 等等,我们也可以通过上面案例的方式创建自己的类去继承 EventEmitter 来实现事件的管理。

												

node.js从入门到放弃(二)的更多相关文章

  1. node.js从入门到放弃《什么是node.js》

    1.什么是node.js Node.js是一个后端的Javascript运行环境(支持的系统包括*nux.Windows),这意味着你可以编写系统级或者服务器端的Javascript代码. Node. ...

  2. node.js从入门到放弃(一)

    以下内容全是我个人理解写出,如有不对,请立刻练习本人进行更改.以免被刚入门的被我带入坑里. —node是什么?我想大家应该都知道. node是前端未来干掉后端的一种语言,是用JavaScript来编写 ...

  3. node.js从入门到放弃《模块》

    在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护. 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很 ...

  4. Node.js快速入门

    Node.js是什么? Node.js是建立在谷歌Chrome的JavaScript引擎(V8引擎)的Web应用程序框架. 它的最新版本是:v0.12.7(在编写本教程时的版本).Node.js在官方 ...

  5. Node.js核心入门

    前言: 因为以前学习Node.js并没有真正意义上的去学习它,而是粗略的学习了npm的常用命令和Node.js一些模块化的语法,因此昨天花了一天的时间看了<Node.js开发指南>一书.通 ...

  6. Node.js之操作文件系统(二)

    Node.js之操作文件系统(二) 1.创建与读取目录 1.1 创建目录 在fs模块中,可以使用mkdir方法创建目录,该方法的使用方法如下: fs.mkdir(path,[mode],callbca ...

  7. Node.js开发入门—使用cookie保持登录

    这次来做一个站点登录的小样例,后面会用到. 这个演示样例会用到Cookie.HTML表单.POST数据体(body)解析. 第一个版本号,我们的用户数据就写死在js文件中. 第二个版本号会引入Mong ...

  8. Node.js开发入门—HelloWorld再分析

    在Node.js开发入门(1)我们用http模块实现了一个简单的HelloWorld站点,这次我们再来细致分析下代码.了解很多其它的细节. 先看看http版本号的HelloWorld代码: 代码就是这 ...

  9. Cookie和Session在Node.JS中的实践(二)

    Cookie和Session在Node.JS中的实践(二) cookie篇在作者的上一篇文章Cookie和Session在Node.JS中的实践(一)已经是写得算是比较详细了,有兴趣可以翻看,这篇是s ...

随机推荐

  1. django-ckeditor表情包修改

    一.版本 Django==1.11 django-ckeditor==5.2.2 二.关键步骤 1.删除旧的ckeditor静态文件 所在目录:项目目录下的static文件夹下的ckditor文件夹 ...

  2. HDU6038:Function(循环群/节+找公式)

    传送门 题意 给出一个\(0\sim n-1\)的排列a,一个\(0\sim {m-1}\)的排列b,询问满足\[f(i)=b_{f(a_i)}~~(0\le i\le n-1)\]的函数的个数 分析 ...

  3. C# 中==和Equal的区别

    http://new-fighter.iteye.com/blog/1634800 今天突然看到一种情况,颠覆了我对这比较使用方法的判断. 于是开始在网上找资料,但几乎都是Java的,好不容易找到一个 ...

  4. Random Query CodeForces - 846F

    题目 翻译: 给出一个n个数字的数列a[1],...,a[n],f(l,r)表示使a[l],a[l+1],...,a[r]组成的新序列中的重复元素只保留一个后,剩下元素的数量(如果l>r,则在计 ...

  5. Kruskal HDOJ 1863 畅通工程

    题目传送门 /* 此题为:HDOJ 1233 + HDOJ 1232 */ #include <cstdio> #include <algorithm> #include &l ...

  6. linux下使用svn创建版本库和权限管理

    linux上的svn服务端如何和本地的电脑客户端结合使用 Linux上安装SVN服务器: 第一步:检查是否已安装 # rpm -qa subversion 第二步: 通过yum命令安装svnserve ...

  7. 题解报告:hdu 4135 Co-prime(容斥定理入门)

    Problem Description Given a number N, you are asked to count the number of integers between A and B ...

  8. sublime的几款实用插件

    1.CSScomb 用于调整css属性的书写顺序 2.Emmet 缩写神器 3.HTML/CSS/JS Prettify 代码格式化 4.Trimmer 去空格去空行 5.Alignment 代码对齐 ...

  9. discuz x2.5用户注册后邮箱认证后无法收到邮件或者直接进垃圾箱

    又是一个周末,jquery特效继续折腾我那discuz论坛,我开启了个邮箱验证,恶意注册的太恶心了,没有办法. 能稍微屏蔽点,但是问题来了,据亲们反应,无法收到验证邮件,或者有时间直接进入垃圾箱,这个 ...

  10. BZOJ3083: 遥远的国度(树链剖分)

    题意 $n$个节点的树,每个点有权值,支持三种操作 1. 换根 2.把$x$到$y$路径上节点权值变为$z$ 3.询问路径最小值 Sol 啥?你说这是TopTree的裸题?那你写去啊 很显然,如果没有 ...