什么是装饰器模式?在 JavaScript 中如何实现?

装饰器模式:在不修改原函数的前提下,动态扩展其功能。

函数装饰器(高阶函数)

// 日志装饰器
function withLog(fn) {
  return function (...args) {
    console.log(`调用 ${fn.name},参数: `, args)
    const result = fn.apply(this, args)
    console.log(`返回: `, result)
    return result
  }
}

// 性能装饰器
function withTimer(fn) {
  return function (...args) {
    console.time(fn.name)
    const result = fn.apply(this, args)
    console.timeEnd(fn.name)
    return result
  }
}

// 缓存装饰器
function withCache(fn) {
  const cache = new Map()
  return function (...args) {
    const key = JSON.stringify(args)
    if (cache.has(key)) return cache.get(key)
    const result = fn.apply(this, args)
    cache.set(key, result)
    return result
  }
}

// 组合使用
const enhancedFn = withCache(withTimer(withLog(complexCalc)))

类装饰器(TC39 提案 / TypeScript)

// TypeScript 中的装饰器
function log(target, key, descriptor) {
  const original = descriptor.value
  descriptor.value = function (...args) {
    console.log(`调用 ${key}`)
    return original.apply(this, args)
  }
  return descriptor
}

class Service {
  @log
  fetchData() { /* ... */ }
}

实际应用

防抖 / 节流本质上就是装饰器:

function debounce(fn, delay) {
  let timer
  return function (...args) {
    clearTimeout(timer)
    timer = setTimeout(() => fn.apply(this, args), delay)
  }
}

function throttle(fn, interval) {
  let lastTime = 0
  return function (...args) {
    const now = Date.now()
    if (now - lastTime >= interval) {
      lastTime = now
      return fn.apply(this, args)
    }
  }
}

// 使用
window.addEventListener('resize', debounce(handleResize, 300))
window.addEventListener('scroll', throttle(handleScroll, 100))

核心思想

同分类其他题目