39 # events 模块的实现原理

news2024/11/22 14:29:52

观察者模式:会有两个类,观察者会被存储到被观察者中,如果被观察者状态变化,会主动通知观察者,调用观察者的更新方法

发布订阅好处:可以解耦合

const EventEmitter = require("events");

// 使用自己实现的 events 类
// const EventEmitter = require("./39/events.js");

const util = require("util");
function Man() {}
// 继承
util.inherits(Man, EventEmitter);
// 创建实例
let man = new Man();

// 发布订阅
const sleep = (name) => console.log(name, "睡");
const eat = (name) => console.log(name, "吃");

newListener 固定写法,每次绑定都会触发,可以用于判断监听了哪些事件:https://nodejs.org/dist/latest-v18.x/docs/api/events.html#event-newlistener

man.on("newListener", (type) => {
    console.log("newListener---->", type);
    if (type === "唱跳rap篮球") {
        // 在当前同步代码执行完毕后触发事件
        process.nextTick(() => {
            man.emit(type, "小坤");
        });
    }
});

// newListener 测试
man.on("唱跳rap篮球", sleep);
man.once("唱跳rap篮球", eat);

在这里插入图片描述

on 方法

man.on("唱跳rap篮球", sleep);
man.on("唱跳rap篮球", eat);
man.emit("唱跳rap篮球", "小坤");

在这里插入图片描述

off 方法

man.on("唱跳rap篮球", sleep);
man.off("唱跳rap篮球", sleep);
man.emit("唱跳rap篮球", "小坤");

在这里插入图片描述

once 方法

man.once("唱跳rap篮球", eat);
man.emit("唱跳rap篮球", "小坤");
man.emit("唱跳rap篮球", "小坤");

在这里插入图片描述

自己实现一个简单版本的 EventEmitter

下面实现一下上面提的五种方法:newListener、on、emit、off、once

function EventEmitter() {
    this._events = {}; // 默认给 EventEmitter 准备的
}

// 订阅
EventEmitter.prototype.on = function (eventName, callback) {
    if (!this._events) this._events = {};
    // 如果不是 newListener 那就需要触发 newListener 的回调
    if (eventName !== "newListener") {
        this.emit("newListener", eventName);
    }
    if (!this._events[eventName]) this._events[eventName] = [];
    this._events[eventName].push(callback);
};
// 发布
EventEmitter.prototype.emit = function (eventName, ...args) {
    if (this._events && this._events[eventName]) {
        this._events[eventName].forEach((event) => event(...args));
    }
};
// 注销
EventEmitter.prototype.off = function (eventName, callback) {
    if (this._events && this._events[eventName]) {
        // 这里需要对 once 里的进行处理:删除时获取 once 里的 l 属性和 callback 比较,如果相等也需要删除
        this._events[eventName] = this._events[eventName].filter((cb) => cb != callback && cb.l != callback);
    }
};
// 订阅只执行一次
EventEmitter.prototype.once = function (eventName, callback) {
    // 使用切片
    const once = (...args) => {
        callback(...args);
        this.off(eventName, once);
    };
    // 给 once 添加 l 属性用于表示 callback
    once.l = callback;
    // 先绑定一个一次性事件,稍后触发时在将事件清空
    this.on(eventName, once);
};

module.exports = EventEmitter;

events 源码快览

先配置 lanch.json

{
    // 使用 IntelliSense 了解相关属性。 
    // 悬停以查看现有属性的描述。
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "启动程序",
            // 这里我们不跳过 node 的内部源码,因为等下需要调试 require,需注释掉
            "skipFiles": [
                // "<node_internals>/**"
            ],
            "program": "${workspaceFolder}\\doc\\39 # events 模块的实现原理.js"
        }
    ]
}

然后调用方法处打上断点,进入单步调试

在这里插入图片描述

on 方法:别名 addListener 底层调用的是 _addListener

在这里插入图片描述

源码这里面写了 Object.create(null),它跟 {} 有区别,它没有原型链,而 {} 是有原型链的。

在这里插入图片描述
newListener 方法:我们可以看到在 _addListener 里面有对 newListener 做处理,会先触发 newListener。

在这里插入图片描述

emit 方法:

在这里插入图片描述

这里需要提到一个 primordials :(primordials 变量是nodejs内部对原生 js 众多类型构造器、方法的一个外观,防止当原生 js 构造器、方法被覆盖时导致出错)这里的 apply 就是来自 primordials 里的 Reflect.apply

在这里插入图片描述

off 方法:别名 removeListener

在这里插入图片描述
也是类似的判断方法:list[i] === listener || list[i].listener === listener
在这里插入图片描述

once 方法:底层调用了 _onceWrap,将 listener 挂在了 wrapped.listener 上,我们自己实现的是挂在 once.l

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/667829.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

提前预体验阿里大模型“通义千问”的方法来了!

随着AI大模型的浪潮席卷全球&#xff0c;如今的AI技术已经颠覆了大家对传统AI的认识&#xff0c;微软更是用浏览器与搜索引擎上的实践&#xff0c;证明了当今的AI技术具备打破行业格局的能力。 对于我们应用开发者来说&#xff0c;AI基建的建设与竞争是无法参与的&#xff0c;…

数据结构——查找

文章目录 **1 查找的基本概念****2 顺序查找和折半查找****2.1 顺序查找****2.2 折半查找****2.3 分块查找** **3 树型查找****3.1 二叉排序树BST****3.1.1 二叉排序树的定义****3.1.2 二叉排序树的查找****3.1.3 二叉排序树的插入****3.1.4 二叉排序树的构造****3.1.5 二叉排序…

C#提升(一、泛型)

一、什么是泛型 泛型&#xff0c;即“参数化类型” 我们来看以下代码&#xff0c;目的很明确&#xff0c;就是显示参数类型&#xff0c;这种类似的代码或者说只有参数类型不同&#xff0c;但是功能相同时&#xff0c;我们如何让代码写的更优雅&#xff1f; 在泛型没有出现的…

LaTeX花式引用章节、图片、公式【有图有代码】

LaTeX花式引用章节、图片、公式【有图有代码】 1 使用~\cite, ~\cref, ~\autoref~\cref~\autoref~\ref 1 使用~\cite, ~\cref, ~\autoref 为什么要使用~ 因为 ~ 符号起到限制换行的作用&#xff0c;通常情况下&#xff0c;LaTeX会根据需要自动确定在引用标签和编号之间的换行点…

Flink 学习二 Flink 编程基础API

Flink 学习二 Flink 编程基础API 1. 基础依赖引入 <dependency><groupId>org.apache.flink</groupId><artifactId>flink-java</artifactId><version>1.14.4</version></dependency><dependency><groupId>org.apa…

MacOS安装与卸载Zookeeper

文章目录 安装1.下载2.移动至/usr/local 目录下3.进入 ZooKeeper 目录4. 拷贝出一份新的配置文件5.启动 ZooKeeper 服务器6.验证 ZooKeeper 是否成功启动 关闭卸载参考 安装 1.下载 https://downloads.apache.org/zookeeper/zookeeper-3.7.1/ 2.移动至/usr/local 目录下 将…

驻波比理解

VSWR(Voltage Standing Wave Ratio)代表电压驻波比。要完全理解这个术语&#xff0c;需要知道什么是“驻波”。 假设两个波长相同的波以相反的方向传播&#xff0c;如下所示。一个波表示为蓝线&#xff0c;它朝着正确的方向旋转。另一个波用绿线表示&#xff0c;它在左方向旋转…

Android大图加载优化方案

我们在编写Android程序的时候经常要用到许多图片&#xff0c;不同图片总是会有不同的形状、不同的大小&#xff0c;但在大多数情况下&#xff0c;这些图片都会大于我们程序所需要的大小。比如微博长图&#xff0c;海报等等。所以我们就要对图片进行局部显示。 大图加载基本需求…

【QQ界面展示-监听键盘事件 Objective-C语言】

一、关于这个通知,我们就说到这里, 1.接下来,就看一下, 我们说了这么一堆,目的是为了什么, 目的是为了监听我们那个键盘的点击事件吧, 我们说了一堆,目的是为了监听我们这个键盘的弹出事件、不是点击事件, 当键盘弹出以后,我们是不是要做一件事儿, 那么,我们知道…

虚拟机网卡/网络配置,静态IP配置

文章目录 1. Vmvare设置 “编辑->虚拟机网络编辑”2. 新建一个虚拟机并给它设置网卡3. 配置eth0网卡为静态IP vim /etc/sysconfig/network-scripts/ifcfg-eth04、测试 1. Vmvare设置 “编辑->虚拟机网络编辑” 这里设置了3个虚拟网络(两个主机模式&#xff0c;这两个网络…

2023.6.20 GPIO子系统编写LED驱动

作业&#xff1a;通过GPIO子系统编写LED驱动&#xff0c;应用程序控制LED灯亮灭 &#xff08;1&#xff09;led.h #ifndef __LED_H__ #define __LED_H__ // typedef struct{ // unsigned int MODER; // unsigned int OTYPER; // unsigned int OSPEEDR; // un…

FreeRTOS实时操作系统(五)临界区及任务调度器

系列文章目录 文章目录 系列文章目录临界区代码保护任务调度器的挂起与保护 临界区代码保护 临界区&#xff1a;是指那些必须要完整运行的&#xff0c;不能被打断的代码 适用于&#xff1a; 1.外设初始化 2.操作系统的代码有很多不能被打断 3.用户自己的需求 一般在中断、任…

014 - STM32学习笔记 - I2C访问存储器(一)

014 - STM32学习笔记 - I2C访问存储器 1、存储器分类 存储器主要分为两类&#xff1a;易失性存储器和非易失性存储器&#xff0c;从字面上理解&#xff0c;判断易失/非易失主要取决于设备掉电后&#xff0c;存储的数据是否会丢失。常规的来说&#xff0c;易失性存储器存取速度…

Django基础入门⑥:Django过滤器和标签讲解

Django基础入门⑥&#xff1a;Django过滤器和标签讲解 Django过滤器过滤器语法过滤器应用获取变量的长度截取指定个数的词返回指定键的排序列表add给变量值加“n” Django url标签url标签动态url Django自定义标签如何自定义标签定义之前的准备工作模块变量register自定义标签赋…

Java 对接google WIFI定位API

1.创建Http请求工具类 1.1.引入httpclient <dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.10</version></dependency> 1.2.封装Http工具类 /*** Http请求* a…

MySQL 高级语句 一

目录 一、MySQL高级&#xff08;进阶&#xff09;SQL语句1. select2. distinct3. where4. and or5. in6. between7. 通配符8. order by9. 函数9.1 数学函数9.2 聚合函数9.3 字符串函数 二、高级查询语句2.1 group by &#xff08;用于分组和汇总&#xff09;2.2 having2.3 别名…

如何在 XMind 中绘制流程图

XMind 是专业强大的思维导图软件,由于其结构没有任何限制,很多朋友特别喜欢用它来绘制流程图。禁不住大家的多次询问,今天 XMind 酱就将这简单的流程图绘图方法分享给大家。 在 XMind 中,绘制流程图的主角是「自由主题」和「联系」。它们可以打破思维导图的限制,让你自由…

《异常检测——从经典算法到深度学习》21 Anomaly Transformer:具有关联差异的时间序列异常检测

We # 《异常检测——从经典算法到深度学习》 0 概论1 基于隔离森林的异常检测算法 2 基于LOF的异常检测算法3 基于One-Class SVM的异常检测算法4 基于高斯概率密度异常检测算法5 Opprentice——异常检测经典算法最终篇6 基于重构概率的 VAE 异常检测7 基于条件VAE异常检测8 Do…

[NX亲测有效]Ubuntu,Jetson nano,NX板开机设置开机自起,Jetson nano,NX设置x11vnc开机自起

&#xff01;&#xff01;Ubuntu,Jetson nano,NX板开机设置开机自起&#xff0c;nano NX设置x11vnc开机自起&#xff01;&#xff01; 1.创建一个rc-local自启服务 2.创建运行脚本 3.启动服务 4.NX&#xff0c;nano设置x11vnc并设置开机自启 大功告成&#xff01;编写不易…

【中级软考】软件设计-考试介绍

一、软考好处 通过软考认证可以抵扣当年的 3600 元的个税,并且有些城市可以积分落户,同时获得证书可以获得同等级别的职称。计算机方向的职称是以考代评,所以获得中级软考证书就相当于获得同等的中级计算机工程师职称,获得高级软考证书就相当于获得同等的高级计算机工程师…