developer-plus / interview

https://interview.developer-plus.org
MIT License
9 stars 1 forks source link

手写题:事件总线 #10

Open Hongbusi opened 2 years ago

Hongbusi commented 2 years ago
class EventBus {
  constructor() {
    this.eventBus = {}
  }

  on(eventName, eventCallback, thisArg) {
    if (typeof eventName !== 'string') {
      throw new TypeError('the event name must be string type')
    }

    if (typeof eventCallback !== 'function') {
      throw new TypeError('the event callback must be function type')
    }

    let handlers = this.eventBus[eventName]
    if (!handlers) {
      handlers = []
      this.eventBus[eventName] = handlers
    }

    handlers.push({
      eventCallback,
      thisArg
    })

    return this
  }

  off(eventName, eventCallback) {
    if (typeof eventName !== 'string') {
      throw new TypeError('the event name must be string type')
    }

    if (typeof eventCallback !== 'function') {
      throw new TypeError('the event callback must be function type')
    }

    const handlers = this.eventBus[eventName]
    if (handlers && eventCallback) {
      const newHandlers = [...handlers]
      for (let i = 0; i < newHandlers.length; i++) {
        const handler = newHandlers[i]
        if (handler.eventCallback === eventCallback) {
          const index = handlers.indexOf(handler)
          handlers.splice(index, 1)
        }
      }
    }

    if (handlers.length === 0) {
      delete this.eventBus[eventName]
    }
  }

  once(eventName, eventCallback, thisArg) {
    if (typeof eventName !== 'string') {
      throw new TypeError('the event name must be string type')
    }

    if (typeof eventCallback !== 'function') {
      throw new TypeError('the event callback must be function type')
    }

    const tempCallback = (...payload) => {
      this.off(eventName, tempCallback)
      eventCallback.apply(thisArg, payload)
    }

    return this.on(eventName, tempCallback, thisArg)
  }

  emit(eventName, ...payload) {
    if (typeof eventName !== 'string') {
      throw new TypeError('the event name must be string type')
    }

    const handlers = this.eventBus[eventName] || []
    handlers.forEach((handler) => {
      handler.eventCallback.apply(handler.thisArg, payload)
    })
    return this
  }
}
betteroneday commented 2 years ago
image

看起来index这行代码似乎并不需要,index和i是一致的。

handlers.splice(i, 1)

Hongbusi commented 2 years ago

使用 splice 删除 handlers 一个元素之后,其他元素的下标可能会发生改变。@betteroneday

const arr = [1, 2, 3, 4]

// 假设有一个 for 循环
arr.splice(i, 1) // i = 0, 此时 arr = [2, 3, 4]
arr.splice(i, 1) // i = 1, 此时 arr = [2, 4]
...
baboon-king commented 2 years ago

mini 版来了


class EventEmitter {
  constructor() {
    this.subs = Object.create(null);
  }

  $on(eventType, handler) {
    this.subs[eventType] = this.subs[eventType] || [];
    this.subs[eventType].push(handler);
  }

  $emit(eventType, ...params) {
    this.subs[eventType].forEach((handler) => handler(...params));
  }
}
const em = new EventEmitter();

function handleClick(params1) {
  console.log("clicked", params1);
}

em.$on("click", handleClick);

em.$on("click", (params1) => {
  console.log("clicked2", params1);
});

em.$on("change", (params1, params2) => {
  console.log(`changed:通过${params1},改变成${params2}`);
});
lyx-jay commented 2 years ago

简单版 EventBus

class EventBUs {
  constructor() {
    this.cache = {};
  }

  on(name, cb) {
    if (this.cache[name]) {
      this.cache[name].push(cb);
    } else {
      this.cache[name] = [cb];
    }
  }

  off(name, cb) {
    let callbacks = this.cache[name];
    if (callbacks) {
      const index = callbacks.findIndex(f => f === cb);
      if (index >= 0) {
        callbacks.splice(index, 1);
      }
    }
  }

  emit(name, once=false, ...args) {
    if (this.cache[name]) {
      let callbacks = this.cache[name].slice();
      for (let cb of callbacks) {
        cb(...args);
      }
      if (once) {
        delete this.cache[name];
      }
    }
  }
}