实现 before 方法
基础知识
什么是
before
函数?before
是一个高阶函数,它接收一个数字n
和一个函数func
作为参数,然后返回一个新函数。这个新函数的功能是:确保原始函数func
在其被调用第n
次之前可以正常执行。从第n
次调用开始,新函数将不再执行func
,而是返回最后一次成功执行func
时的结果。简单来说,
before(n, func)
创建了一个最多只能成功调用n - 1
次的函数。与
once
的关系once
函数是before
函数的一个特例。一个只允许执行一次的函数once(func)
,其行为等价于before(2, func)
。因为before(2, func)
允许在第 2 次调用 之前 执行,即只允许成功调用第 1 次。应用场景
before
函数非常适用于那些需要限制执行次数的场景,特别是对于一些一次性或有限次数的初始化、设置或事件绑定。- 一次性初始化: 确保一个复杂的初始化逻辑只执行一次。
const init = before(2, setupApp);
- 有限的免费试用: 比如一个功能只允许用户免费试用 3 次。
- 防止重复绑定: 确保某个事件监听器只被绑定有限的次数。
- 一次性初始化: 确保一个复杂的初始化逻辑只执行一次。
核心思路
before
函数的实现核心,与 debounce
和 throttle
类似,都依赖于闭包来维护一个持久化的状态。
高阶函数与闭包:
before(n, func)
作为高阶函数,会返回一个新的函数。这个新函数通过闭包,可以持续访问并修改定义在before
函数作用域内的变量。计数器状态: 我们需要一个计数器(就是参数
n
本身)来追踪剩余的可执行次数。每当返回的新函数被调用时,这个计数器就减一。条件执行: 在新函数内部,通过一个
if
语句来检查计数器的值。只有当计数器的值仍然大于 0 时,才执行原始函数func
。结果缓存: 为了实现“第 n 次调用及之后,返回最后一次成功执行的结果”,我们需要另一个闭包变量(例如
result
)来存储func
最后一次成功执行时的返回值。当执行次数耗尽后,后续的调用将直接返回这个缓存的result
。
代码实现
下面是一个 before
函数的清晰实现。
/**
* 创建一个函数,这个函数在被调用 n 次之前,会一直调用 func;
* 第 n 次及之后,将返回最后一次调用 func 的结果。
* @param {number} n - 函数可被调用的次数上限(不包括第 n 次)
* @param {Function} func - 需要限制执行次数的函数
* @returns {Function} - 返回一个新的函数
*/
function before(n, func) {
let result; // 1. 用于缓存最后一次成功执行的结果
// 参数校验
if (typeof func !== 'function') {
throw new TypeError('Expected a function');
}
// 2. 返回一个新的包装函数
return function (...args) {
// 3. 每次调用时,先将 n 减 1,然后判断是否大于 0
if (--n > 0) {
// 4. 如果次数未耗尽,则执行 func,并缓存结果
result = func.apply(this, args);
}
// 5. 如果次数已耗尽(n <= 0),则 func 将不再执行
// 无论是耗尽还是未耗尽,都返回 result
// 对于耗尽后的调用,将一直返回上一次缓存的结果
return result;
};
}
代码解析
闭包变量初始化:
let result
作为闭包变量,存储func
最后一次有效调用的返回值,初始化为undefined
。- 该变量通过闭包被
before
函数返回的包装函数持久化共享,实现结果缓存。
包装函数结构:
before
核心是返回新函数,捕获n
和result
变量,形成持久化状态空间。- 包装函数接收参数并处理调用逻辑,控制原始函数
func
的执行次数。
执行次数控制逻辑:
- 核心条件:
if (--n > 0)
,利用前置递减运算符--n
先减 1 再判断。 - 示例说明:
before(3, myFunc)
调用过程:- 第 1 次:
n=3→2
,2>0
成立,执行myFunc
。 - 第 2 次:
n=2→1
,1>0
成立,执行myFunc
。 - 第 3 次:
n=1→0
,0>0
不成立,不执行。 - 第 4 次及以后:
n≤0
,均不执行。
- 第 1 次:
- 实现“在第
n
次调用前执行”的逻辑,即总共执行n-1
次。
- 核心条件:
函数执行与结果缓存:
- 当
--n > 0
成立时,通过result = func.apply(this, args)
执行func
。 apply
确保func
执行时this
上下文正确,参数传递无误。- 每次执行结果存入闭包变量
result
,实现结果缓存。
- 当
结果返回机制:
- 执行次数未耗尽时,返回当前
func
调用结果result
。 - 执行次数耗尽后,直接返回最后一次成功调用的
result
(缓存值),后续调用均返回该值,保证结果一致性。
- 执行次数未耗尽时,返回当前