JS装饰器:fn(...args)与fn.apply(this,arguments)差异解析
JS装饰器两种实现的差异解答
核心差异前提
你自行实现的decorate版本返回值是箭头函数,教程给出的wrap版本返回值是普通function,两者的行为差异本质来自箭头函数和普通函数的固有特性区别,和是否使用apply、是否使用剩余参数传参没有直接关系。
什么场景必须用wrap写法,不能用decorate写法
只要满足以下任意一种场景,箭头函数实现的decorate就会出现逻辑错误,必须使用返回普通函数的wrap写法:
- 被装饰的是对象方法、类原型方法,且方法内部依赖
this访问实例/对象上的属性 - 被装饰的是构造函数,需要通过
new关键字调用执行 - 装饰逻辑需要依赖函数调用时动态绑定的
this做判断或处理
典型失效场景示例:
const calculator = { base: 10, sum(...args) { return args.reduce((acc, cur) => acc + cur, 0) + this.base } } const running = () => console.log('Running') // 用decorate包装sum方法 calculator.sum = decorate(calculator.sum, running) console.log(calculator.sum(1,2,3)) // 运行直接报错:Cannot read property 'base' of undefined/Window // 原因是箭头函数的this在定义时就固定绑定到外层全局作用域,不会随调用方变化,拿不到calculator实例上的base属性
如果换成wrap版本包装上述方法,代码可以正常输出Running和计算结果16,不会出现this指向错误。
fn.apply(this, arguments)的运行逻辑
这行代码的核心作用是保证原函数fn执行时的上下文、入参与未被包装时的表现完全一致,拆解运行逻辑如下:
apply是所有Function原型上自带的方法,作用是指定函数执行时的this指向,同时以类数组/数组形式批量传入执行参数- 第一个传入的
this:因为外层wrap返回的是普通函数,普通函数的this是调用时动态绑定的——作为对象方法调用时指向调用对象,new调用时指向新创建的实例,被call/apply/bind修改时指向手动指定的值。把这个动态this透传给apply,就能让原函数fn拿到和未包装时完全一致的执行上下文 - 第二个传入的
arguments:是普通函数内部自带的类数组对象,会存储调用包装函数时传入的所有实参,和函数定义时有没有写对应形参无关。把arguments传给apply后,所有入参会按顺序原封不动传给fn执行
补充:现代ES6运行环境下不需要依赖老旧的
arguments对象,用普通函数+剩余参数+apply/call可以实现效果完全一致的包装逻辑,可读性更高:const wrapModern = (fn, callback) => function(...args) { callback() return fn.apply(this, args) // 等价写法:return fn.call(this, ...args) }这个版本和教程给出的
wrap行为完全一致,核心点只是把返回值从箭头函数换成普通函数,保留了this的动态绑定能力,参数传递用剩余参数实现更直观。
内容的提问来源于stack exchange,提问作者Khaled
相关产品推荐
相关产品推荐

