如何在不重新赋值的前提下为JavaScript函数追加后续执行动作
实现无需重新赋值的函数动作追加方法
你原本的实现是每次生成新的包装函数返回,所以需要手动赋值覆盖原变量。JS中无法直接修改已定义普通函数的内部执行逻辑,但是可以通过两种方案实现你要的效果:
方案1:规范实现(生产环境可用)
基于继承Function的自定义可执行函数类实现,无hack,稳定性高,仅需要在定义函数时做一层包装:
class ExtendableFunction extends Function { constructor(originalFn) { // 生成可执行函数的基础结构 super('...args', 'return this._execute(...args)'); // 缓存原始函数和动作队列 this._originalFn = originalFn; this._actionQueue = []; // 绑定this指向避免调用时上下文丢失 return this.bind(this); } // 实际执行逻辑:先跑原始函数,再依次跑队列内的所有追加动作 _execute(...args) { const result = this._originalFn(...args); this._actionQueue.forEach(action => action(...args)); return result; } // 追加动作直接写入队列即可,无需返回新函数 appendAction(action) { this._actionQueue.push(action); } } // 用法示例 const a = new ExtendableFunction(() => { console.log("foo"); }); a(); // 输出:foo a.appendAction(() => console.log("bar")); a(); // 输出:foo \n bar
如果需要兼容普通函数,可以加一个转换工具方法:
function toExtendable(fn) { return new ExtendableFunction(fn); } // 普通函数转换示例 function originalA() { console.log("foo"); } const a = toExtendable(originalA);
方案2:Hack实现(无需修改原有函数定义)
利用调用栈提取变量名,自动给上层作用域的原变量赋值,无需手动写赋值语句,适合快速调试使用,不推荐生产环境用(依赖JS引擎的调用栈格式):
Function.prototype.appendAction = function(action) { const originalFn = this; // 生成包装后的新函数 const wrappedFn = function(...args) { originalFn(...args); action(...args); }; // 拷贝原函数的所有属性、原型、toString等保持行为一致 Object.assign(wrappedFn, originalFn); wrappedFn.prototype = originalFn.prototype; wrappedFn.toString = () => originalFn.toString(); wrappedFn.length = originalFn.length; // 从调用栈提取调用appendAction的变量名 const callerStackLine = new Error().stack.split('\n')[1]; const variableName = callerStackLine.match(/([\w$]+)\.appendAction/)[1]; // 自动赋值覆盖上层作用域的原变量 eval(`${variableName} = wrappedFn`); }; // 用法示例,完全符合你要的效果 function a() { console.log("foo"); } a(); // 输出:foo a.appendAction(() => console.log("bar")); // 无需手动赋值 a(); // 输出:foo \n bar
内容的提问来源于stack exchange,提问作者MrMythical
相关产品推荐
相关产品推荐

