Hand-shredded event bus and second global data sharing library

A global data sharing tool based on event bus

Take you to hand-write event bus and encapsulate global data sharing library

Comments are articles

EventBus

//Global event bus
// Author:coderwhy(mentor)
// Rewrite:coderJoon(little fan)
class EventBus {<!-- -->
  constructor() {<!-- -->
    // Object to store events
    this.EventBus = {<!-- -->};
  }
  /**
   * Listen for events
   * @param {*} eventName event name
   * @param {*} eventCallback event callback
   * @param {*} thisArg this points to
   */
  on(eventName, eventCallback, thisArg) {<!-- -->
    //Type checking
    if (typeof eventName !== 'string') {<!-- -->
      throw new TypeError('eventname must be string type');
    }

    if (typeof eventCallback !== 'function') {<!-- -->
      throw new TypeError('eventCallback must be function type');
    }
    // Get the corresponding data source according to the event name
    let handlers = this.EventBus[eventName];
    // It may be empty the first time. If it cannot be obtained, we need to create a data structure and push the data structure into it.
    if (!handlers) {<!-- -->
      //Create data structure
      handlers = [];
      //Put the data structure in
      this.EventBus[eventName] = handlers;
    }
    //Add event callback and this pointer
    handlers.push({<!-- -->
      eventCallback,
      thisArg,
    });
    // The purpose of returning this is for possible chain calls later.
    return this;
  }
  /**
   * trigger event
   * @param {*} eventName trigger event name
   * @param {...any} payload The data passed by the trigger event
   */
  emit(eventName, ...payload) {<!-- -->
    if (typeof eventName !== 'string') {<!-- -->
      throw new TypeError('eventname must be string type');
    }
    // Find the corresponding event callback according to the event name to trigger the function
    // 1. Get the event data structure
    // tips: The reason for giving it || is to prevent it from being undefined and to prevent errors.
    const handlers = this.EventBus[eventName] || [];
    handlers.forEach((handler) => {<!-- -->
      handler.eventCallback.apply(handler.thisArg, payload);
    });
    return this;
  }
  /**
   * Cancel monitoring
   * @param {*} eventName event name
   * @param {*} eventCallback event callback
   */
  off(eventName, eventCallback) {<!-- -->
    if (typeof eventName !== 'string') {<!-- -->
      throw new TypeError('eventname must be string type');
    }
    if (typeof eventCallback !== 'function') {<!-- -->
      throw new TypeError('eventcallback must be function type');
    }
    //The hardest part
    // 1. First get the data structure corresponding to the event name
    const handlers = this.EventBus[eventName]; //No need for || because there is a judgment below
    if (handlers & amp; & amp; eventCallback) {<!-- -->
      // Get the data object
      const newHandlers = [...handlers];
      //The data structure is xxx:[{eventcallback,thisarg},...]
      // Traverse this structure
      for (let i = 0; i < newHandlers.length; i + + ) {<!-- -->
        const handler = newHandlers[i]; //Each object
        if (handler.eventCallback === eventCallback) {<!-- -->
          const index = handlers.indexOf(handler);
          handlers.splice(index, 1);
        }
      }
    }

    if (handlers.length === 0) {<!-- -->
      delete this.EventBus[eventName];
    }
  }
  /**
   * Only listen once
   * @param {*} eventName event name
   * @param {*} eventCallback event callback
   * @param {*} thisArg this points to
   */
  once(eventName, eventCallback, thisArg) {<!-- -->
    if (typeof eventName !== 'string') {<!-- -->
      throw new TypeError('eventname must be string type');
    }
    if (typeof eventCallback !== 'function') {<!-- -->
      throw new TypeError('eventcallback must be function');
    }
    //How do we write a function that only listens once?
    // 1. Be sure to perform monitoring
    // 2. Cancel all previous monitoring
    /*
    Implementation ideas:
    As long as you call my once method, you will definitely perform monitoring.
    But before executing the monitoring, I need to cancel all the previous monitoring.
    When emit is executed, we need to call this function to execute the callback
    Watch me operate
     */
    const tempfunction = (...payload) => {<!-- -->
      this.off(eventName, tempfunction); //Cancel listening
      eventCallback.apply(thisArg, payload); //Execute function
    };
    //Execute monitoring logic
    return this.on(eventName, tempfunction, thisArg);
    /*
    Questions you may ask
    Teacher, there is nothing wrong with you passing the eventname. Your tempfunction is passed to my off method. It uses eventcallback to determine whether to cancel. How do you cancel it? You have a bug?
    Answer: Look at this.on below to see if temfunction is pushed as a callback function, so the tem function can be canceled normally.
     */
  }

  clear() {<!-- -->
    this.EventBus = {<!-- -->};
  }

  hasEvent(eventName) {<!-- -->
    return Object.keys(this.EventBus).includes(eventName);
  }
}
module.exports = EventBus;
//Global event bus methods
/*
on---listen for events
once---monitor once
emit---trigger event
off---Cancel monitoring
clear---Clear event
hasEvent---whether it contains the event
 */

EventStore

//Introduce event bus
const EventBus = require('./EventBus');
//Introduce tools
const {<!-- --> isObject } = require('./utils');
class EventStore {<!-- -->
  //Initialize data--receive an opstion object
  constructor(opstion) {<!-- -->
    // Type judgment
    if (!isObject(opstion.state)) {<!-- -->
      throw new TypeError('state must be object type');
    }
    if (opstion.action & amp; & amp; isObject(opstion.action)) {<!-- -->
      // Get all actions for judgment
      const values = Object.values(opstion.action);
      for (const value of values) {<!-- -->
        if (typeof value !== 'function') {<!-- -->
          throw new TypeError('action must be function type');
        }
      }
      //Add to class
      this.action = opstion.action;
    }
    //Add the data in opstion to the class
    this.state = opstion.state;
    //Create two event buses for notifications
    this.even = new EventBus();
    this.evens = new EventBus();
    // Monitor state data
    this.observe(opstion.state);
  }
  observe(state) {<!-- -->
    // Get this
    const _this = this;
    // Get the key on the state and then get the data source based on the key
    Object.keys(state).forEach((key) => {<!-- -->
      let Orderdata = this.state[key]; //Get each data-old data
      //Call the object.defineproperty method to monitor the data
      Object.defineProperty(state, key, {<!-- -->
        get: function () {<!-- -->
          return Orderdata;
        },
        set: function (NewData) {<!-- -->
          if (Orderdata === NewData) return;
          // 1. Update data
          Orderdata = NewData;
          // 2. Notification event
          _this.even.emit(key, Orderdata);
          _this.evens.emit(key, {<!-- --> [key]: Orderdata });
        },
      });
    });
  }
  /**
   * Listening function
   * @param {*} stateKey key in the monitored state
   * @param {*} stateCallback processing function after listening
   */
  onState(stateKey, stateCallback) {<!-- -->
    const keys = Object.keys(this.state);
    if (keys.indexOf(stateKey) === -1) {<!-- -->
      throw new Error('the key does not in state');
    }
    if (typeof stateCallback !== 'function') {<!-- -->
      throw new TypeError('the eventCallback must be function type');
    }
    this.even.on(stateKey, stateCallback);
    const data = this.state[stateKey];
    stateCallback.apply(this.state, [data]);
  }
  /**
   * Monitor multiple keys
   * @param {*} stateKeys multiple keys
   * @param {*} stateCallback a callback
   */
  onStates(stateKeys, stateCallback) {<!-- -->
    if (typeof stateKeys !== 'object') {<!-- -->
      throw new TypeError('the onStates keys must be Array type');
    }
    const keys = Object.keys(this.state);
    const value = {<!-- -->};
    for (const key of stateKeys) {<!-- -->
      if (keys.indexOf(key) === -1) {<!-- -->
        throw new Error('the state dose node have your key');
      }
      this.evens.on(key, stateCallback);
      value[key] = this.state[key];
    }
    stateCallback.apply(this.state, [value]);
  }
  /**
   * Set data
   * @param {*} stateKey setkey
   * @param {*} stateValue setdata
   */
  setState(stateKey, stateValue) {<!-- -->
    this.state[stateKey] = stateValue;
  }
  /**
   * Cancel listening function s version
   * @param {*} stateKeys keys that need to be canceled
   * @param {*} stateCallback The bound callback
   */
  offStates(stateKeys, stateCallback) {<!-- -->
    const keys = Object.keys(this.state);
    if (typeof stateKeys !== 'object') {<!-- -->
      throw new TypeError('the keys must be Array type');
    }
    stateKeys.forEach((key) => {<!-- -->
      if (keys.indexOf(key) === -1) {<!-- -->
        throw new TypeError('the key does not in state');
      }
      this.evens.off(key, stateCallback);
    });
  }
  /**
   * Cancel the listening function
   * @param {*} statekey key that needs to be canceled
   * @param {*} stateCallback The bound callback
   */
  offState(statekey, stateCallback) {<!-- -->
    const keys = Object.keys(this.state);
    if (keys.indexOf(statekey) === -1) {<!-- -->
      throw new Error('the state does not have your key');
    }
    this.even.off(statekey, stateCallback);
  }
  /**
   * Execute asynchronously
   * @param {*} actionName asynchronous name
   * @param {...any} args The parameters you want to pass
   */
  dispatch(actionName, ...args) {<!-- -->
    if (typeof actionName !== 'string') {<!-- -->
      throw new TypeError('the action must be string type');
    }
    if (Object.keys(this.action).indexOf(actionName) === -1) {<!-- -->
      throw new Error('the action dont in stateAction');
    }
    // Call the function through this
    const actionFun = this.action[actionName];
    //The second parameter is ctx. The purpose is to pass the state to make modifications and our payload.
    actionFun.apply(this, [this.state, ...args]);
  }
}
module.exports = EventStore;