基于JavaScript实现一个简单的事件触发器

 更新时间:2024年01月30日 08:02:45   作者:JYeontu  
这篇文章主要为大家详细介绍了如何使用JavaScript实现一个简单的事件触发器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

说在前面

简单实现一个事件触发器

题目描述

设计一个 EventEmitter 类。这个接口与 Node.js 或 DOM 的 Event Target 接口相似,但有一些差异。EventEmitter 应该允许订阅事件和触发事件。

你的 EventEmitter 类应该有以下两个方法:

subscribe - 这个方法接收两个参数:一个作为字符串的事件名和一个回调函数。当事件被触发时,这个回调函数将被调用。 一个事件应该能够有多个监听器。当触发带有多个回调函数的事件时,应按照订阅的顺序依次调用每个回调函数。应返回一个结果数组。你可以假设传递给 subscribe 的回调函数都不是引用相同的。 subscribe 方法还应返回一个对象,其中包含一个 unsubscribe 方法,使用户可以取消订阅。当调用 unsubscribe 方法时,回调函数应该从订阅列表中删除,并返回 undefined。

emit - 这个方法接收两个参数:一个作为字符串的事件名和一个可选的参数数组,这些参数将传递给回调函数。如果没有订阅给定事件的回调函数,则返回一个空数组。否则,按照它们被订阅的顺序返回所有回调函数调用的结果数组。

示例 1:

//输入: actions = ["EventEmitter", "emit", "subscribe", "subscribe", "emit"], 
values = [[], ["firstEvent", "function cb1() { return 5; }"],  ["firstEvent", "function cb1() { return 5; }"], ["firstEvent"]]
//输出: [[],["emitted",[]],["subscribed"],["subscribed"],["emitted",[5,6]]]
//解释:
const emitter = new EventEmitter();
emitter.emit("firstEvent"); // [], 还没有订阅任何回调函数
emitter.subscribe("firstEvent", function cb1() { return 5; });
emitter.subscribe("firstEvent", function cb2() { return 6; });
emitter.emit("firstEvent"); // [5, 6], 返回 cb1 和 cb2 的输出

示例 2:

//输入: actions = ["EventEmitter", "subscribe", "emit", "emit"], 
values = [[], ["firstEvent", "function cb1(...args) { return args.join(','); }"], ["firstEvent", [1,2,3]], ["firstEvent", [3,4,6]]]
//输出: [[],["subscribed"],["emitted",["1,2,3"]],["emitted",["3,4,6"]]]
//解释: 注意 emit 方法应该能够接受一个可选的参数数组。

const emitter = new EventEmitter();
emitter.subscribe("firstEvent, function cb1(...args) { return args.join(','); });
emitter.emit("firstEvent", [1, 2, 3]); // ["1,2,3"]
emitter.emit("firstEvent", [3, 4, 6]); // ["3,4,6"]

示例 3:

//输入: actions = ["EventEmitter", "subscribe", "emit", "unsubscribe", "emit"], 
values = [[], ["firstEvent", "(...args) => args.join(',')"], ["firstEvent", [1,2,3]], [0], ["firstEvent", [4,5,6]]]
//输出: [[],["subscribed"],["emitted",["1,2,3"]],["unsubscribed",0],["emitted",[]]]
//解释:
const emitter = new EventEmitter();
const sub = emitter.subscribe("firstEvent", (...args) => args.join(','));
emitter.emit("firstEvent", [1, 2, 3]); // ["1,2,3"]
sub.unsubscribe(); // undefined
emitter.emit("firstEvent", [4, 5, 6]); // [], 没有订阅者

示例 4:

//输入: actions = ["EventEmitter", "subscribe", "subscribe", "unsubscribe", "emit"], 
values = [[], ["firstEvent", "x => x + 1"], ["firstEvent", "x => x + 2"], [0], ["firstEvent", [5]]]
//输出: [[],["subscribed"],["emitted",["1,2,3"]],["unsubscribed",0],["emitted",[7]]]
//解释:
const emitter = new EventEmitter();
const sub1 = emitter.subscribe("firstEvent", x => x + 1);
const sub2 = emitter.subscribe("firstEvent", x => x + 2);
sub1.unsubscribe(); // undefined
emitter.emit("firstEvent", [5]); // [7]

提示:

  • 1 <= actions.length <= 10
  • values.length === actions.length
  • 所有测试用例都是有效的。例如,你不需要处理取消一个不存在的订阅的情况。
  • 只有 4 种不同的操作:EventEmitteremitsubscribe 和 unsubscribe 。 EventEmitter 操作没有参数。
  • emit 操作接收 1 或 2 个参数。第一个参数是要触发的事件名,第二个参数传递给回调函数。
  • subscribe 操作接收 2 个参数,第一个是事件名,第二个是回调函数。
  • unsubscribe 操作接收一个参数,即之前进行订阅的顺序(从 0 开始)。

解题思路

这是一个简单的事件触发器实现,通过subscribe方法订阅指定事件并注册回调函数,通过emit方法触发指定事件,并执行所有相关回调函数。此外,subscribe方法返回一个包含取消订阅函数的对象,可以在需要取消订阅时调用。

具体来说,该事件触发器实现主要有以下几个方法:

  • constructor:构造函数,创建一个Map实例来存储事件及其对应的回调函数列表。
  • subscribe:订阅指定事件并注册回调函数,返回一个包含取消订阅函数的对象。
  • unsubscribe:取消订阅指定事件的回调函数。
  • emit:触发指定事件,并执行所有相关回调函数。下面是这些方法的详细说明:

constructor

constructor(){
    this.eventMap = new Map();
}

构造函数,创建一个Map实例来存储事件及其对应的回调函数列表。

subscribe

subscribe(eventName, callback) {
    const eventList = this.eventMap.get(eventName) || [];
    const key = Math.ceil(Math.random() * 10000);
    eventList.push({
        key,
        callback
    });
    this.eventMap.set(eventName,eventList);
    return {
        unsubscribe: () => {
            const eventList = this.eventMap.get(eventName) || [];
            const index = eventList.findIndex(item=>item.key === key);
            if(index === -1) return;
            eventList.splice(index,1);
            if(eventList.length === 0) this.eventMap.delete(eventName);
            this.eventMap.set(eventName,eventList);
        }
    };
}

订阅指定事件并注册回调函数,返回一个包含取消订阅函数的对象。

该方法首先获取指定事件对应的回调函数列表,如果不存在则新建一个空列表。然后,生成一个随机数作为回调函数的唯一键,并将其与回调函数一起存入回调函数列表中。最后,更新事件对应的回调函数列表,并返回一个包含unsubscribe方法的对象。

unsubscribe

unsubscribe: () => {
    const eventList = this.eventMap.get(eventName) || [];
    const index = eventList.findIndex(item=>item.key === key);
    if(index === -1) return;
    eventList.splice(index,1);
    if(eventList.length === 0) this.eventMap.delete(eventName);
    this.eventMap.set(eventName,eventList);
}

取消订阅指定事件的回调函数。

该方法首先获取指定事件对应的回调函数列表,然后在列表中查找指定键的回调函数,并将其从列表中删除。如果删除后回调函数列表为空,则删除该事件对应的键值对。

emit

emit(eventName, args = []) {
    const eventList = this.eventMap.get(eventName);
    if(!eventList) return [];
    const res = [];
    eventList.forEach(fn=>{
        res.push(fn.callback(...args));
    })
    return res;
}

触发指定事件,并执行所有相关回调函数。

该方法首先获取指定事件对应的回调函数列表,如果不存在则返回空数组。然后,对于列表中的每个回调函数,按照顺序执行其对应的函数,并将返回值存入一个数组中。最后,返回保存了所有返回值的数组。

完整代码

class EventEmitter {
    constructor(){
        this.eventMap = new Map();
    }
    /**
     * @param {string} eventName
     * @param {Function} callback
     * @return {Object}
     */
    subscribe(eventName, callback) {
        const eventList = this.eventMap.get(eventName) || [];
        const key = Math.ceil(Math.random() * 10000);
        eventList.push({
            key,
            callback
        });
        this.eventMap.set(eventName,eventList);
        return {
            unsubscribe: () => {
                const eventList = this.eventMap.get(eventName) || [];
                const index = eventList.findIndex(item=>item.key === key);
                if(index === -1) return;
                eventList.splice(index,1);
                if(eventList.length === 0) this.eventMap.delete(eventName);
                this.eventMap.set(eventName,eventList);
            }
        };
    }
    
    /**
     * @param {string} eventName
     * @param {Array} args
     * @return {Array}
     */
    emit(eventName, args = []) {
        const eventList = this.eventMap.get(eventName);
        if(!eventList) return [];
        const res = [];
        eventList.forEach(fn=>{
            res.push(fn.callback(...args));
        })
        return res;
    }
}

//  const emitter = new EventEmitter();

//  function onClickCallback() { return 99 }
//  const sub = emitter.subscribe('onClick', onClickCallback);
 
//  let res = emitter.emit('onClick'); // [99]
//  sub.unsubscribe(); // undefined
// res = emitter.emit('onClick'); // []

到此这篇关于基于JavaScript实现一个简单的事件触发器的文章就介绍到这了,更多相关JavaScript事件触发器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vscode使用Prettier Code插件的详细教程

    vscode使用Prettier Code插件的详细教程

    这篇文章主要介绍了vscode使用Prettier Code插件的详细教程,本文通过图文实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-09-09
  • html 锁定页面(js遮罩层弹出div效果)

    html 锁定页面(js遮罩层弹出div效果)

    html 锁定页面(js遮罩层弹出div效果),需要的朋友可以参考下。
    2009-10-10
  • js 获取当前web应用的上下文路径实现方法

    js 获取当前web应用的上下文路径实现方法

    下面小编就为大家带来一篇js 获取当前web应用的上下文路径实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-08-08
  • 利用JavaScript实现动态表格功能实例

    利用JavaScript实现动态表格功能实例

    在现代网页开发中,表格是展示数据的常用元素,无论是数据统计、信息展示还是数据录入,表格都能以整齐的格式展示出大量信息,这篇文章主要介绍了利用JavaScript实现动态表格功能的相关资料,需要的朋友可以参考下
    2026-03-03
  • JavaScript中清空数组的三种方法分享

    JavaScript中清空数组的三种方法分享

    JavaScript中清空数组的三种方法分享,需要的朋友可以参考下。
    2011-04-04
  • 使用JS实现抖音上很火的圣诞树的示例代码

    使用JS实现抖音上很火的圣诞树的示例代码

    圣诞节快到了,经常会在抖音上刷到圣诞树的视频,所以本文小编给大家介绍了如何使用JS实现圣诞树,文章通过代码示例给大家介绍的非常详细,感兴趣的小伙伴跟着小编一起来看看吧
    2023-12-12
  • js 淘宝首页的拼音排序效果

    js 淘宝首页的拼音排序效果

    模仿淘宝首页的拼音排序效果代码。
    2009-05-05
  • 前端碎片localStorage的清除方法

    前端碎片localStorage的清除方法

    这篇文章主要介绍了前端localStorage清除方法的相关资料,localStorage是前端存储数据的工具,数据不会因为页面刷新而消失,但测试后需要清除数据,清除localStorage的方法包括手动清除、编程方式清除、使用浏览器提供的清除功能和使用第三方库,需要的朋友可以参考下
    2024-11-11
  • Javascript中的对象和原型(二)

    Javascript中的对象和原型(二)

    这篇文章给大家介绍了js中的对象和原型,从工厂模式,构造函数方面展开话题,介绍的非常详细,具有参考借鉴价值,需要的朋友可以参考下
    2016-08-08
  • js字符串分割处理的几种方法(6种)

    js字符串分割处理的几种方法(6种)

    本文主要介绍了js字符串分割处理的几种方法,主要介绍了6种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06

最新评论