闭包可让 filter 回调“记住”状态,实现去重、限数量取值、相邻关系判断等带上下文的过滤逻辑,但需注意状态独立性、重置时机及同步性限制。

可以通过在闭包中保存状态变量,让 filter 回调函数“记住”之前的操作结果或累计信息,从而实现带上下文记忆的过滤逻辑。
用闭包缓存已见过的值(去重过滤)
常见需求是保留首次出现的元素,后续重复项跳过。此时闭包可封装一个 Set 或对象来记录已处理过的键值:
- 定义外部作用域的
seen = new Set() - 返回的过滤回调中检查并更新
seen,只保留未见过的项 - 每次调用该 filter 函数都复用同一份
seen状态
const uniqueById = () => {
const seen = new Set();
return item => {
if (seen.has(item.id)) return false;
seen.add(item.id);
return true;
};
};
const data = [{id: 1}, {id: 2}, {id: 1}, {id: 3}];
data.filter(uniqueById()); // [{id: 1}, {id: 2}, {id: 3}]
用闭包维护计数器(取前 N 个满足条件的元素)
当需要“最多取 3 个偶数”这类带数量限制的过滤时,普通 filter 无法中断,但闭包可封装计数逻辑:
- 在闭包内初始化
count = 0和上限limit - 回调中先判断是否已达上限,再决定是否保留当前项并递增计数
- 同一个闭包实例多次调用会持续累加
count
const takeFirstNEvens = (n) => {
let count = 0;
return num => {
if (count >= n) return false;
if (num % 2 === 0) {
count++;
return true;
}
return false;
};
};
[1, 2, 4, 5, 6].filter(takeFirstNEvens(2)); // [2, 4]
用闭包保存上一项(相邻关系判断)
若需基于“前一个通过过滤的元素”做判断(如只保留比前一个大的数),闭包可暂存上一次通过的值:
- 闭包中声明
lastKept,初始为undefined或最小值 - 回调中比较当前项与
lastKept,满足条件则更新lastKept并返回true - 注意:这种逻辑本质是“流式筛选”,结果依赖遍历顺序
const strictlyIncreasing = () => {
let lastKept = -Infinity;
return num => {
if (num > lastKept) {
lastKept = num;
return true;
}
return false;
};
};
[1, 3, 2, 4, 3, 5].filter(strictlyIncreasing()); // [1, 3, 4, 5]
注意事项与边界提醒
闭包记忆虽灵活,但需注意几点:
- 每次调用工厂函数(如
uniqueById())才生成独立的状态,避免多个 filter 共享同一闭包导致意外耦合 - 状态不会自动重置,如需重复使用,应重新调用工厂函数获取新过滤器
- 对异步或并发场景不适用——
filter是同步一次性执行,闭包变量不是线程安全的 - 过度依赖闭包状态会让逻辑变隐晦,关键状态建议显式命名并注释用途

















