什么是装饰器模式?在 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))核心思想
- 开闭原则:对扩展开放,对修改封闭
- 装饰器比继承更灵活,避免类爆炸