用 includes 去重慢是因为其时间复杂度为 O(n²),而用 Set.prototype.has 可降至 O(1);前者每次遍历结果数组,后者基于哈希表查找,实测快 5–20 倍。

大数组去重用 includes 为什么慢?
因为每次判断元素是否已存在,都要从头遍历当前结果数组,时间复杂度是 O(n²)。10 万条数据可能触发上亿次比较,CPU 明显卡顿。
典型写法:if (!result.includes(item)) result.push(item) —— 这是性能瓶颈的根源。
-
includes底层是线性扫描,和数组长度正相关 - 结果数组越长,后续每次
includes越慢 - V8 引擎不会对普通数组做哈希优化,无法跳过比对
用 Set 的 has 替代 includes 怎么写?
Set 内部用哈希表实现,has 平均时间复杂度是 O(1),查 100 万次和查 10 次耗时几乎一样。
关键不是「用 Set 构造」,而是「用 has 做存在性判断」——你完全可以保留原数组结构,只把判重逻辑换掉:
const seen = new Set();
const result = [];
for (const item of arr) {
if (!seen.has(item)) {
seen.add(item);
result.push(item);
}
}
- 不要写
[...new Set(arr)]—— 它会丢弃原始顺序,且无法处理对象等引用类型 -
seen.has(item)比result.includes(item)快一个数量级以上(实测 5–20 倍) - 注意:
Set对NaN和0的处理与严格相等一致,但{} !== {},对象仍需序列化或用 WeakMap
has 在不同数据类型下的行为差异
Set.prototype.has() 判等逻辑和 === 一致,但有几个易错点:
- 基本类型(
string、number、boolean)完全可靠 -
NaN能正确识别:set.has(NaN)返回true(这是Set特有的,===不行) - 对象、函数、数组:按引用判等,
[1,2]和[1,2]被视为不同项 - 若需深比较去重,不能直接用
has,得先JSON.stringify或用库生成唯一 key
实际项目中容易忽略的内存与兼容性问题
高频调用时,Set 实例本身会持续占用内存,而 includes 是临时扫描、无长期持有。
- Node.js 12+ 和所有现代浏览器都支持
Set.prototype.has,IE 完全不支持(需core-jspolyfill) - 如果数组含大量重复项,
seen的 size 会远小于原数组,内存反而更省 - 避免在循环外复用同一个
Set实例,除非你明确要跨批次累积去重状态 - 超大数组(如 500 万+)建议分块处理,否则单次循环可能触发 JS 引擎的 long task 报警
真正影响耗时的从来不是语法糖,而是底层数据结构的选择。用 has 不是“换个写法”,是把 O(n²) 查找换成 O(1) 哈希探查——这个切换点,藏在你每次写 includes 的地方。










