前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库Set 与数组元素存在性检查性能
JSJavaScript数组与集合

频繁检查成员存在时,Set.has 一定比 includes 更快吗?

真正要比较的是整段工作:建立集合、查询多少次,以及数据变化后要重建多少次。

前端进阶之旅 · 一题精讲更新于 2026.09.06
JavaScript#数组与集合#性能优化#构建工具
先看核心答案读代码示例
理解线索

按工作负载估算是否值得建索引

  1. 输入规模数组扫描可能访问多少候选
  2. 查询次数一次构建能被多少次查询复用
  3. 更新成本源数据变化时怎样保持索引一致

下面例子验证两种写法的业务结果一致,不包含计时,也不声称测得某个环境下的性能提升。

核心回答

先记住这个答案

数组 includes 通常按顺序查找,未命中或目标靠后时需要检查很多位置;Set 的规范要求平均访问时间优于线性,常见实现能提供很快的成员查询,但不强制所有实现每次都是 O(1)。对同一批较大数据进行多次查询,建立一次 Set 并复用往往值得;只查一次或每次都重建 Set,构建和内存成本可能抵消收益。还要确认两种数据结构的相等语义符合业务,并把更新同步、命中分布和目标引擎纳入测量,不能只跑一个 has 循环就给出普遍结论。

  • 把索引构建和重复查询一起计入成本
  • 规范要求平均次线性而非固定实现
  • 查询收益依赖数据规模、命中位置与更新频率

先让两种实现完成相同工作

示例用一组允许编号过滤查询列表,数组版本每次 includes,集合版本先创建一个索引再 has。结果一致才有比较成本的意义,否则所谓更快可能只是漏掉了某些业务条件。

这里索引创建放在查询外部,表示允许集合在本轮查询中不变。如果把 new Set 放进 filter 回调,每个查询都重新读取整份输入,就失去了复用数据结构的主要价值。

一次建索引供多次成员查询JavaScript
const allowed = ['a', 'c', 'e'];
const queries = ['e', 'x', 'a', 'x'];
const scanned = queries.filter(value => allowed.includes(value));
const index = new Set(allowed);
const indexed = queries.filter(value => index.has(value));
console.log(JSON.stringify(scanned));
console.log(JSON.stringify(indexed));
查看输出与解释
["e","a"]
["e","a"]

两种方法保留相同查询项和顺序,Set 只构建一次。这个小输入用于验证语义,不足以说明实际速度差异,真实性能需要覆盖代表性规模与索引生命周期。

命中位置与构建频率会改变选择

数组查询经常命中第一项时,扫描可能很快结束;大量未命中则更容易付出完整扫描成本。小集合的额外分配和常数开销也可能占主导,不能只用最坏复杂度判断所有短数组。

若允许列表频繁更新,应比较增量维护 Set、整体重建和直接扫描的总成本。还需要决定两份结构由谁负责同步,源数组已修改而索引仍旧会产生错误结果,比少量性能差异更重要。

公平测量需要保留真实输入和输出用途

测量应包含冷启动或稳定阶段的实际比例、不同查询分布,以及结果是否真的被消费。只选择有利于某一种写法的输入,或把构建成本放在计时范围外,会得到难以应用的数字。

内存、垃圾回收和峰值延迟也要观察,尤其在服务请求里每次创建大集合的场景。最终选择应有可复现环境和数据规模说明,避免把单机一次短测试写成跨浏览器的固定性能倍率。

回答前,多想一步

容易答错的地方

把 Set.has 每次 O1 当作规范承诺
规范约束是平均访问时间优于线性,允许不同内部数据结构。可以说明常见实现的预期效率,但不要承诺最坏情况或所有引擎实现,更不能用未经测量的倍率替代实际验证。
只测查询而遗漏反复构建集合
若真实路径每次都重新创建 Set,构建和分配可能是主要成本。应按真实生命周期测量整段工作,先确认索引能够复用,再讨论单次 has 与 includes 的差异。
试着用自己的话回答

面试官还会怎么问?

Set 与 includes 的 NaN 判断一样吗?

二者使用的相等语义都能识别 NaN,并把正负零视为相同,对象仍按身份。业务若按字段内容匹配,需要额外提取稳定键,不能因为查询结果类型相同就忽略身份模型。

只查询一次应该直接用 includes 吗?

通常可以先采用清楚的直接扫描,但仍取决于集合是否已经存在和输入规模。若上游本来就维护 Set,就没有额外构建成本;选择应围绕现有数据形态和真实查询需求。

同时需要顺序列表和快速查询怎么办?

可以维护数组与 Set 两种视图,但必须指定唯一更新入口或从同一版本生成,防止不一致。若数据规模很小,双结构维护复杂度未必值得,应结合实际热点和可维护性判断。

从一道题,走向一组知识

把知识连起来

数组与集合

Map.size 与 Object.keys().length 统计的到底是什么?

比较集合条目数量与对象属性计数的成本和范围。

数组与集合

Set 怎样判重,为什么 NaN 会合并而两个相同对象不会?

先验证成员相等规则符合要比较的业务结果。

参考资料

  • MDN:Set 描述与性能边界

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 先让两种实现完成相同工作
  3. 命中位置与构建频率会改变选择
  4. 公平测量需要保留真实输入和输出用途
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑