JS手写

82 阅读2分钟

节流

可类比成技能冷却,在事件触发后n秒内不能再次触发,常用场景是用户频繁点击 image.png 代码实现

/**
 * 节流
 * @param {*} fn 事件处理器
 * @param {*} time 冷却时长
 * @returns 
 */
function throttle (fn, time) {
  let timer = null
  return function () {
    const args = arguments
    if (timer) return
    fn.apply(this, args)
    timer = setTimeout(() => {
      timer = null
    }, time);
  }
}

防抖

可类比成回城,在回城成功之前被打断就需要重新回城,在事件触发n秒后才会执行处理函数,如果n秒内再次触发,就需要重新计时,常用场景有搜索框输入、修改浏览器窗口大小

const debounce = (fn, time) => {
  let timer = null
  return function () {
    const args = arguments
    if (timer) {
      clearTimeout(timer) // 立即阻止定时器回调函数的执行
    }
    timer = setTimeout(() => {
      fn.apply(this, args)
      timer = null
    }, time);
  }
}

发布订阅模式

/**
 * ? 什么是发布订阅模式
 * 订阅者把想订阅的事件注册到调度中心,发布者发布该事件到调度中心,也就是该事件触发时,由调度中心统一调度
 * 1. 创建一个类 2. 在这个类里面创建一个调度中心  3. on方法(把函数添加到调度中心)off方法(取消订阅)emit方法(发布者发布事件到调度中心,调度中心处理代码) 4. 确定入参
 */

class EventHub {
  constructor() {
    // 消息队列
    this.message = {}
  }
  /**
   * 订阅
   * @param {*} type 事件类型
   * @param {*} callback 回调
   */
  on (type, callback) {
    // 入队,为每个事件类型创建一个数组
    this.message[type] = this.message[type] || []
    this.message[type].push(callback)
  }
  /**
   * 发布
   * @param {*} type 事件类型
   * @param {*} data 参数
   * @returns 
   */
  emit (type, data) {
    const fnList = this.message[type] // alias
    if (!fnList) return
    // 对消息队列进行轮询,依次执行
    fnList.map(f => f.call(undefined, data))
  }
  /**
   * 取消订阅
   * @param {*} type 事件类型
   * @param {可选} callback 回调
   * @returns 
   */
  off (type, callback) {
    const fnList = this.message[type]
    if (!fnList) { return }
    // 如果没有传callback就将type对应的缓存都清空
    if (!callback) {
      fnList = undefined
    }
    // 如果有callback,就遍历缓存列表,删除callback这个方法 或 被once拦截的方法
    let fn
    for (let i = 0; i < fnList.length; i++) {
      fn = fnList[i]
      if (fn === callback || fn.callback === callback) {
        fnList.splice(i, 1)
        break
      }
    }
  }
  /**
   * 监听一次
   * @param {*} type 
   * @param {*} callback 
   */
  once (type, callback) {
    // 重写callback,执行后从消息队列中删除
    let onceFn = () => {
      callback.call(undefined, arguments)
      this.off(type, onceFn)
    }
    // 
    onceFn.callback = callback // 在事件未emit的情况下,off时访问传入的callback
    this.on(type, onceFn) // 注册事件
  }
}


const handlerA = () => { console.log('handlerA'); }
const handlerB = () => { console.log('handlerB'); }
const handlerC = () => { console.log('handlerC'); }


const person = new EventHub()
// 注册
person.on('buy', handlerA)
person.on('buy', handlerB)
// 取消订阅
person.off('buy', handlerA)
// 触发
person.emit('buy')
// 监听一次
person.once('buy', handlerA)
// 取消订阅
person.off('buy', handlerA)
// 触发
person.emit('buy')
console.log(person);