zoukankan      html  css  js  c++  java
  • 手动实现一个eventEmitter

           观察者模式在前端开发中非常常用,我们经常用的事件就是观察者模式的一种体现。它对我们解耦模块、开发基于消息的业务起着非常重要的作用。Node.js 原生自带 EventEmitter 模块,可见它的重要性。
          作为在工作中经常遇到和面试的经典题目当然要琢磨透彻,下面一步步手动写一个eventEmitter
    首先,我们要知道EE的api是什么样的
    node的EventEmitter包含了很多常用的API,我们一一来介绍几个实用的API.
    方法名
    方法描述
    addListener(event, listener)
    为指定事件添加一个监听器到监听器数组的尾部。
    prependListener(event,listener)
    与addListener相对,为指定事件添加一个监听器到监听器数组的头部。
    on(event, listener)
    其实就是addListener的别名
    once(event, listener)
    为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。
    removeListener(event, listener)
    移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器
    off(event, listener)
    removeListener的别名
    removeAllListeners([event])
    移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。
    setMaxListeners(n)
    默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于提高监听器的默认限制的数量。
    listeners(event)
    返回指定事件的监听器数组。
    emit(event, [arg1], [arg2], [...])
    按参数的顺序执行每个监听器,如果事件有注册监听返回 true,否则返回 false。
    现在,我们来主要实现on,emit,removeListener 等几个api 

     

    每一个EventEmitter实例都有一个包含所有事件的对象_events,
    事件的监听和监听事件的触发,以及监听事件的移除等都在这个对象_events的基础上实现。
     
    首先实现一个EventEmitter类
     
    class EventEmitter {
    constructor() {
    this._events = Object.create(null); // 定义事件的存储对象
    this._eventsCount = 0;
    }
    }
     
    接着实现添加事件监听的on方法:
    1. 为每个事件名称添加一个数组作为事件的存储器
    2. 事件由一个对象来保存,为了区别开on和once需要为每个事件增加一个isOnce字段
    //添加事件监听
    on(eventName, fn, isOnce = false) {
    if (typeof fn !== "function") {
    throw new TypeError("The listener must be a function!");
    }
    if (!this._events[eventName]) {
    this._events[eventName] = [];
    this._events[eventName].push({ fn, isOnce });
    } else {
    this._events[eventName].push({ fn, isOnce }); // 存入监听的事件名和事件
    }
    } 
     
    实现事件触发方法emit:
    1. 找到事件名称下面的所有事件以此触发
    2. 当事件的isOnce 为true的时候在触发事件之后清空事件
    // 事件触发
    emit(eventName, ...args) {
    if (!this._events[eventName]) {
    return false;
    }
    const len = this._events[eventName].length;
    for (let i = 0; i < len; i++) {
    let event = this._events[eventName][i];
    event.fn.call(this, ...args);
    if (event.isOnce) {
    this.removeListener(eventName, event.fn);
    i--;
    }
    }
    }
    

     

    事件监听解绑
    1.清空事件名下的监听的函数,函数为undefined则清空事件名下所有事件
    // 移除监听事件
    removeListener(eventName, fn) {
    if (!this._events[eventName]) return this;
    if (!fn) {
    delete this._events[eventName];
    return this;
    } else {
    this._events[eventName].forEach((item, index) => {
    if (item.fn === fn) {
    this._events[eventName].splice(index, 1);
    } else {
    return this;
    }
    });
    }
    }
    

      

    全部代码实现
     
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>手动实现一个eventEmitter</title>
    </head>
    <body>
    <script>
    class EventEmitter {
    constructor() {
    this._events = Object.create(null); // 定义事件的存储对象
    this._eventsCount = 0;
    }
    //添加事件监听
    on(eventName, fn, isOnce = false) {
    if (typeof fn !== "function") {
    throw new TypeError("The listener must be a function!");
    }
    if (!this._events[eventName]) {
    this._events[eventName] = [];
    this._events[eventName].push({ fn, isOnce });
    } else {
    this._events[eventName].push({ fn, isOnce }); // 存入监听的事件名和事件
    }
    }
     
    //一次性事件监听
    once(eventName, fn) {
    this.on(eventName, fn, true);
    }
     
    // 事件触发
    emit(eventName, ...args) {
    if (!this._events[eventName]) {
    return false;
    }
    const len = this._events[eventName].length;
    for (let i = 0; i < len; i++) {
    let event = this._events[eventName][i];
    event.fn.call(this, ...args);
    if (event.isOnce) {
    this.removeListener(eventName, event.fn);
    i--;
    }
    }
    }
    // 移除监听事件
    removeListener(eventName, fn) {
    if (!this._events[eventName]) return this;
    if (!fn) {
    delete this._events[eventName];
    return this;
    } else {
    this._events[eventName].forEach((item, index) => {
    if (item.fn === fn) {
    this._events[eventName].splice(index, 1);
    } else {
    return this;
    }
    });
    }
    }
    // off:removeListener 的别名
    off(eventName, fn) {
    this.removeListener(eventName, fn);
    }
    // 移除所有监听事件
    removeAllListener(eventName) {
    if (eventName) {
    if (this._events[eventName]) {
    this._events[eventName].length = 0;
    }
    } else {
    this._events = Object.create(null);
    }
    }
    }
     
    function add(...args) {
    let num = 0;
    for (let i = 0; i < args.length; i++) {
    num += args[i];
    }
    console.log(num);
    return num;
    }
     
    let event = new EventEmitter();
    // event.on("adding", add);
    // event.emit("adding", 1, 2, 3, 4);
    // event.removeListener("adding", add);
    // event.emit("adding", 1, 2, 3, 4);
    // event.once("adding", add);
    event.emit("adding", 1, 2, 3, 4);
    event.emit("adding", 1, 2, 3, 4);
    </script>
    </body>
    </html>
    

      

  • 相关阅读:
    轮播 margin-left实现
    点击按钮切换图片
    运用把不同的方式排版,涉及到float box-flox box-orient
    chrome中font-size<12px时并不更改字体大小仍未12px
    js实现跑马灯
    支付宝支付集成
    前端技术博客
    在iphone5/5s出现界面显示不全,大小为iphone4/4s 的问题
    UIImage使用总结
    在IOS开发中使用自定义的字体
  • 原文地址:https://www.cnblogs.com/chrissong/p/10341219.html
Copyright © 2011-2022 走看看