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

请求解析David Walsh的once函数及自定义实现的相关疑问

拆解David Walsh的once函数及你的实现疑问

先看David Walsh的once原函数

function once(fn, context) {
  var result;
  return function() {
    if (fn) {
      result = fn.apply(context || this, arguments);
      fn = null;
    }
    return result;
  };
}

这个函数的核心作用就是让传入的fn只执行一次:第一次调用返回的包装函数时,执行原函数并缓存结果;后续调用直接返回缓存值,不再触发原函数。


关于result = fn.apply(context || this, arguments);的细节

1. context的作用

context是可选参数,用来手动指定原函数fn执行时的this指向。比如你想让fn内部的this绑定到某个特定对象,就把这个对象传给context。

2. 为什么用context || this?

这是典型的兜底逻辑:

  • 如果调用once时传了context,就用这个指定对象作为fn的this;
  • 如果没传context,就用当前包装函数被调用时的this(也就是包装函数执行上下文里的this)。

3. 这里的this指向什么?

这个this是包装函数被调用时的上下文,举个实际例子:

const obj = {
  num: 20,
  getNum: once(function() { return this.num; })
};
obj.getNum(); // 返回20,此时包装函数的this指向obj,所以fn里的this也跟着指向obj

如果直接全局调用包装函数(比如const func = once(fn); func();),非严格模式下this指向全局对象(浏览器是window,Node.js是global),严格模式下是undefined。


你的once函数:fn.apply(this, arguments)怎么获取参数?

假设你的实现类似这样:

function myOnce(fn) {
  let called = false;
  let result;
  return function() {
    if (!called) {
      result = fn.apply(this, arguments);
      called = true;
    }
    return result;
  };
}

// 调用示例
function add(a, b) { return a + b; }
const addOnce = myOnce(add);
addOnce(1, 2); // 返回3

这里的arguments是包装函数(也就是addOnce)被调用时接收的参数集合,是一个类数组对象。apply方法的第二个参数会被拆解成单个参数传给原函数fn——比如调用addOnce(1,2)时,arguments就是{0:1, 1:2, length:2},fn.apply(this, arguments)就等价于执行add(1,2)。


额外补充:为什么要缓存result?

不管原函数有没有返回值,第一次执行后把结果存起来,后续调用直接返回这个值,保证每次调用包装函数的返回结果都和第一次一致——哪怕原函数有副作用(比如修改外部变量),也只会触发一次。

内容的提问来源于stack exchange,提问作者Amnesiac

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 10:40:45