实现 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(缓存值),后续调用均返回该值,保证结果一致性。
- 执行次数未耗尽时,返回当前