请求解析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
相关产品推荐
相关产品推荐

