如何实现JavaScript的spy函数:重载原函数并触发回调
解决JavaScript spy函数的回调触发与返回值问题
看起来你已经尝试了几种实现方式,但要么回调只执行一次,要么返回值不对,咱们一步步来解决这个问题。
先分析你之前代码的问题
第一个版本的问题:
你最初的spy函数在定义时就直接调用了callback,然后返回原函数fun。这意味着只有当你执行Math.abs = spy(Math.abs, cb)这一步时,回调会被触发一次,后续每次调用Math.abs都只会执行原函数,不会再触发回调——这就是为什么只打印一次的原因。function spy(fun, callback) { if (typeof callback == 'function') { callback(fun); // 这里只在spy被调用时执行一次 } return fun; // 返回的是原函数,后续调用和回调无关 }第二个版本的问题:
你用箭头函数包装后,虽然回调能每次触发,但返回的是原函数fun本身,而不是原函数执行后的结果。比如调用Math.abs(-32)时,得到的是Math.abs函数对象,而不是计算后的32,这就导致返回值类型错误。function spy(fun, callback) { return (typeof callback == 'function') ? () => {callback(); return fun} : fun; // 返回的箭头函数里,return fun是返回原函数,不是fun()的结果 }
正确的spy函数实现
我们需要返回一个包装函数,这个函数会在每次被调用时:
- 先执行指定的回调
- 再调用原函数,把原函数的参数和上下文传递过去
- 最后返回原函数的执行结果
代码如下:
function spy(fun, callback) { // 返回一个新的包装函数,接收所有传入的参数 return function(...args) { // 检查回调是函数的话就执行 if (typeof callback === 'function') { callback(); } // 调用原函数,用apply传递上下文和参数,保证原函数行为不变 return fun.apply(this, args); }; }
测试验证
用你的示例代码测试:
function cb() { console.log("The call back has been called"); } Math.abs = spy(Math.abs, cb); var result = Math.abs(-32); // 控制台打印:The call back has been called result = Math.abs(-32); // 控制台再次打印:The call back has been called console.log(result); // 输出32,和原Math.abs的返回值一致
这样就完美实现了需求:每次调用被包装后的Math.abs时,都会触发回调,同时返回正确的计算结果。
内容的提问来源于stack exchange,提问作者GNUSupporter
相关产品推荐
相关产品推荐

