JS手写代码之柯里化和发布订阅EventEmitter


实现柯里化

原理是利用闭包把传入参数保存起来,当传入参数的数量足够执行函数时,就开始执行函数。

const curry = (fn) =>
  (_curry = (...args) =>
    // args.length 获取的时调用时传入的参数个数
    // fn.length 获取的是定义fn时的形参个数
    // 判断实际收集的参数是否达到fn定义时形参的数量,
    // 如果达到则运行函数,如果没达到则返回一个函数并继续手机新参数
    args.length >= fn.length
      ? fn(...args)
      : (...newArgs) => _curry(...args, ...newArgs));
const sum = (a, b, c, d) => a + b + c + d;
const currySum = curry(sum);
console.log(currySum(1, 2, 3, 4));
console.log(currySum(1, 2)(3, 4));

实现发布订阅 EventEmitter

// subs是EventEmitter私有属性,通过on注册事件,off注销事件,emit触发事件
class EventEmitter {
  // ES6实例属性新写法,等价于
  constructor() {
    this.subs = {};
  }
  // #subs = {};
  // {event:[cbs]}
  emit(event, ...args) {
    if (this.subs[event] && this.subs[event].length) {
      this.subs[event].forEach((cb) => cb(...args));
    }
  }
  on(event, cb) {
    (this.subs[event] || (this.subs[event] = [])).push(cb);
  }
  off(event, offCb) {
    if (offCb) {
      if (this.subs[event] && this.subs[event].length)
        this.subs[event] = this.subs[event].filter((cb) => cb !== offCb);
    } else {
      this.subs[event] = [];
    }
  }
}

const event = new EventEmitter();
const cb = function (name) {
  console.log(`hello ${name}`);
};
event.on("say", cb);
event.off("say", cb);
event.emit("say", "bar");

实现发布订阅 EventEmitter(2021-07 更新)

// 发布订阅模式
// on emit once off

// 构造函数版本
function EventEmitter(){
    this._events={}
}

EventEmitter.prototype.on = function(eventName,callback){
    if(!this._events) this._events={};
    if(this._events[eventName]){
        this._events[eventName].push(callback)
    }else{
        this._events[eventName] = [callback]
    }
}

// 触发订阅的事件
EventEmitter.prototype.emit = function(eventName,...args){
    if(!this._events) this._events={};
    const callbacks = this._events[eventName];
    if(callbacks) callbacks.forEach(callback => callback(...args));
}
// 移除订阅的事件
EventEmitter.prototype.off = function(eventName,callback){
    if(!this._events) this._events={};
    const callbacks = this._events[eventName];
    if(callbacks) this._events[eventName] = callbacks.filter(fn => fn !== callback&&fn.l !==callback)
}
// 订阅一次之后自动取消
EventEmitter.prototype.once = function(eventName,callback){
    const one = (...args)=>{
        callback(...args);
        this.off(eventName,one)
    }
    // 自定义属性,和原本的callback相关联,用于取消订阅
    one.l = callback;
    this.on(eventName,one)
}

// class版本
class EventEmitter {
    _events={};
    on(event,listener){
        this._events[event]?this._events[event].push(listener):this._events[event] = [listener];
    }
    emit(event,...args){
        const listeners = this._events[event];
        if(listeners) listeners.forEach(callabck => callabck(...args));
    }
    off(event,listener){
        const listeners = this._events[event];
        if(listeners) this._events[event] = listeners.filter((callback)=>callback !== listener && callback.l!==listener)
    }
    once(event,listener){
        const once = (...args)=>{
            listener(...args);
            this.off(event,once)
        }
        once.l = listener;
        this.on(event,once)
    }
}
module.exports = EventEmitter;

参考

初、中级前端应该要掌握的手写代码实现


文章作者: CassielLee
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 CassielLee !
评论
  目录