You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现JavaScript的spy函数:重载原函数并触发回调

解决JavaScript spy函数的回调触发与返回值问题

看起来你已经尝试了几种实现方式,但要么回调只执行一次,要么返回值不对,咱们一步步来解决这个问题。

先分析你之前代码的问题

  1. 第一个版本的问题:
    你最初的spy函数在定义时就直接调用了callback,然后返回原函数fun。这意味着只有当你执行Math.abs = spy(Math.abs, cb)这一步时,回调会被触发一次,后续每次调用Math.abs都只会执行原函数,不会再触发回调——这就是为什么只打印一次的原因。

    function spy(fun, callback) {
      if (typeof callback == 'function') {
        callback(fun); // 这里只在spy被调用时执行一次
      }
      return fun; // 返回的是原函数,后续调用和回调无关
    }
    
  2. 第二个版本的问题:
    你用箭头函数包装后,虽然回调能每次触发,但返回的是原函数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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 19:37:33