skip to content
月与羽

Js 中的装饰器

/ 5 min read

1. 装饰器是什么?

它是一个包装函数

  • 输入:一个函数(例如 sum)。
  • 输出:一个的、功能更强的函数。

这个新函数会在内部调用原始的 sum 函数,但在调用前后可以附加一些新行为,比如记录日志、检查权限或缓存结果。

一句话比喻:你有一个“做咖啡”的函数,装饰器就像是给它加了一个“拉花”的功能。核心还是做咖啡,但结果被美化(装饰)了。

2. 如何实现?关键技巧:apply

在装饰器内部,当你准备调用原始函数时,可能会遇到两个问题:

  1. this 指向丢失:如果原始函数依赖 this(比如是一个对象的方法),直接调用会使 this 指向错误。
  2. 参数传递麻烦:你需要把所有参数原封不动地传递给原始函数。

解决方案就是下面这行“魔法”代码:

// 在包装函数内部...
return originalFunction.apply(this, arguments);

这行代码做了两件事,实现了完美转发

  • this:把当前上下文(this)传给原始函数,确保指向正确。
  • arguments:把所有接收到的参数,作为一个列表,全部传给原始函数。

这样,原始函数收到的上下文和参数,就和没有被装饰时一模一样。

3. 现代最佳实践:使用 ...args

arguments 是一个老式且有点笨拙的类数组对象。在现代 JavaScript 中,我们用剩余参数 (...args) 来替代它。

...args 会将所有传入的参数收集到一个真正的数组中,代码更简洁、易读。

所以,现代的完美转发通常写成这样:

// 包装函数接收参数
function(...args) {
// ... 在这里添加新功能 ...
// 用 apply 调用原始函数,并传入 context 和参数数组
return originalFunction.apply(this, args);
}

4. 装饰器常见使用场景

  1. 缓存 (Caching / Memoization)

    • 场景:对于计算成本高的函数(如斐波那契数列、复杂计算),用装饰器将结果缓存起来。下次使用相同参数调用时,直接返回缓存的结果,避免重复计算。
  2. 日志与分析 (Logging & Analytics)

    • 场景:在不侵入业务代码的情况下,记录函数的调用时间、输入参数、返回值或异常信息,用于调试或用户行为分析。
  3. 性能监控 (Performance Timing)

    • 场景:自动计算并报告一个函数执行所花费的时间,帮助定位性能瓶颈。
  4. 权限校验 (Access Control)

    • 场景:在执行核心功能(如删除文章、修改数据)前,用装饰器检查当前用户是否拥有足够权限。如果没有,则直接中断操作。
  5. 延迟与防抖 (Debouncing & Throttling)

    • 场景:控制函数的执行频率。例如,防止用户在输入框中每输入一个字符就触发一次 API 请求(防抖),或者在滚动页面时限制事件处理函数的触发频率(节流)。
  6. 参数校验 (Validation)

    • 场景:在函数执行前,自动检查传入的参数是否符合类型、格式或范围要求,让业务逻辑更纯粹。