前端进阶之旅前端进阶之旅
  • 基础篇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 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库includes 与 indexOf 检测 NaN 语义
JSJavaScript数组与集合

数组 includes 和 indexOf 能检测 NaN 吗?两者在语义上有哪些差异?

includes 能检测 NaN,因为它用 SameValueZero 算法;indexOf 用严格相等 ===,而 NaN !== NaN,所以永远找不到。

前端进阶之旅 · 一题精讲更新于 2026.09.05
JavaScript#数组与集合
先看核心答案读代码示例
理解线索

两个比较算法的差异点

  1. includes 的算法SameValueZero:NaN 等于自身,+0 等于 -0
  2. indexOf 的算法严格相等 ===:NaN 不等于任何值包括自身
  3. 引用类型一致两者都比较引用同一性,不做深比较

只要数组可能含 NaN,存在性判断应选 includes 而非 indexOf。

核心回答

先记住这个答案

Array.prototype.includes 使用 SameValueZero 比较算法:除 NaN 视为等于自身外,其余行为与 === 一致(+0 和 -0 视为相等)。因此 [NaN].includes(NaN) 返回 true。indexOf 使用严格相等比较,NaN === NaN 为 false,所以 [NaN].indexOf(NaN) 返回 -1。两者对 +0、-0 的处理相同,对引用类型的比较语义也一致;主要差异在 NaN 上,另外在稀疏数组上 includes 会把空位当作 undefined 参与比较,而 indexOf 会跳过空位。

  • includes 用 SameValueZero,能匹配 NaN
  • indexOf 用 ===,NaN 永远返回 -1
  • 对 +0、-0 和引用,两者行为一致

SameValueZero 与严格相等在 NaN 上的分歧

indexOf 内部按索引逐个用严格相等 === 比对目标值。ECMAScript 规定 NaN 不等于自身,这是 IEEE 754 浮点数规范中 NaN 的定义使然,任何 === 比较都无法命中 NaN。所以无论数组里有多少个 NaN,indexOf(NaN) 的结果恒为 -1,这不是实现缺陷而是规范行为。

includes 是 ES2016 引入的,内部使用 SameValueZero 算法。它在 === 基础上只改了一条规则:NaN 视为与自身相等。+0 与 -0 依然视为相等,这一点和 === 一致。Set 和 Map 的键比较也用 SameValueZero,所以 new Set([NaN]).has(NaN) 同样为 true。

NaN 与零值在两种方法下的行为JavaScript
const arr = [1, NaN, -0, 'x'];
console.log(arr.includes(NaN));
console.log(arr.indexOf(NaN));
console.log(arr.includes(0));
console.log(arr.indexOf(0));
console.log(NaN === NaN);
console.log(Object.is(NaN, NaN));
查看输出与解释
true
-1
true
2
false
true

可直接在任意现代 JS 环境运行。indexOf(0) 命中 -0 返回索引 2,说明零值符号不影响两个方法;Object.is 是 SameValue 算法,与 SameValueZero 的区别只在零值符号。

清洗数值列表时判断是否存在非法计算结果

某数据处理模块接收用户表单数值,解析后得到 [12, NaN, 7],其中 NaN 来自 Number('abc') 的转换失败。提交前需要判断是否存在解析失败项并提示用户。输入约束:数组元素均为 number 类型,长度在数千以内。决策:用 values.includes(NaN) 做存在性检查,命中则拦截提交并定位错误字段。

若写成 values.indexOf(NaN) !== -1,条件永远为假,非法数据会绕过校验直接入库,属于隐蔽的逻辑错误。选择 includes 后判断语义正确,时间复杂度 O(n),与 indexOf 相同,没有额外代价。这也是代码评审中值得固定下来的约定:数值数组的存在性检查一律用 includes。

该差异失效或不够用的条件

在一般数组(无稀疏空位)上,算法差异只在目标是 NaN 时才体现。若搜索的是对象、数组等引用类型,两个方法都只比引用同一性,includes({a:1}) 找不到数组中结构相同但不同引用的对象。若目标是 0,两者都把 -0 当命中;需要区分 +0 与 -0 时只能用 Object.is 配合 findIndex,includes 无能为力。

另外 includes 只返回布尔值,拿不到位置;需要 NaN 的下标时,indexOf 帮不上忙,应改用 findIndex(Number.isNaN)。稀疏数组上 includes 把空位当作 undefined 参与比较,[,].includes(undefined) 为 true,这一点容易被误判为数组里真的有显式的 undefined 值。

定位 NaN 与零值符号区分JavaScript
const arr = [1, NaN, -0];
console.log(arr.findIndex(Number.isNaN));
console.log(arr.findIndex(function (v) { return Object.is(v, -0); }));
console.log([,].includes(undefined));
查看输出与解释
1
2
true

可直接运行。findIndex 接收断言函数,配合 Number.isNaN 或 Object.is 能表达 includes 无法表达的精细比较;空位按 undefined 处理是 includes 的规范行为。

回答前,多想一步

容易答错的地方

认为 indexOf(NaN) 能找到第一个 NaN
indexOf 内部用 === 比较,NaN === NaN 为 false,结果恒为 -1。需要位置时应写 findIndex(Number.isNaN),需要存在性时用 includes(NaN)。
以为 includes 与 indexOf 只差返回值
除了返回值(布尔 vs 下标),比较算法也不同:includes 用 SameValueZero 能匹配 NaN;indexOf 用 === 不能。对 +0/-0 和引用类型两者才完全一致。
试着用自己的话回答

面试官还会怎么问?

`Set` 和 `Map` 判断 `NaN` 键时用哪种语义?

两者都用 SameValueZero,与 includes 一致:new Set([NaN]).has(NaN) 为 true,+0 与 -0 视为同一键。这与 Object.is 的 SameValue 不同,后者区分零值符号。

如何不用 includes 判断一个数组是否含 NaN?

可用 arr.some(Number.isNaN)。Number.isNaN 不做类型转换,只对真正的 NaN 返回 true,比全局 isNaN 安全;语义与 arr.includes(NaN) 等价,但 includes 写法更直观。

`includes` 的 fromIndex 参数有什么注意点?

负值表示从末尾倒数的位置,但搜索方向仍从头到尾;fromIndex >= length 时直接返回 false 不搜索。若负值绝对值超过长度,按 0 处理即搜全数组,例如 [1,2,3].includes(1, -100) 为 true。

从一道题,走向一组知识

把知识连起来

数组与集合

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

Set 去重与 includes 共用 SameValueZero,对照阅读可统一理解 NaN 与 ±0 的比较语义

数组与集合

查找数组元素位置时 findIndex 与 indexOf 分别适用于什么场景?

当 indexOf 找不到 NaN 需要位置时,findIndex 配合断言函数正是替代方案

放回知识体系中复习JavaScript 进阶知识

参考资料

  • Array.prototype.includes()

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

本题目录
  1. 先记住这个答案
  2. SameValueZero 与严格相等在 NaN 上的分歧
  3. 清洗数值列表时判断是否存在非法计算结果
  4. 该差异失效或不够用的条件
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

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

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