前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库includes 与 indexOf 检测 NaN 语义
JavaScript数组与集合

数组的 includes 和 indexOf 能找到 NaN 吗?两者还有哪些差别?

includes 能找到 NaN,而 indexOf 找不到。includes 用的是 SameValueZero 比较,它和 === 只有一个区别:认为 NaN 等于 NaN。而 indexOf 用的就是 ===,而 NaN === NaN 是 false。

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

JavaScript 里三种「相等」

  1. === 严格相等NaN 不等于自身,+0 等于 -0,用它的是 indexOf
  2. SameValueZeroNaN 等于自身,+0 等于 -0,用它的有 includes、Set、Map
  3. Object.isNaN 等于自身,+0 不等于 -0,最严格

三种比较在对象上表现一样,都只看是不是同一个引用。内容相同的两个对象,用哪个方法都找不到。

核心回答

先记住这个答案

includes 能检测到 NaN,而 indexOf 不能。includes 使用 SameValueZero 比较,它和 === 基本一样,唯一的区别是把 NaN 视为等于 NaN,所以 [NaN].includes(NaN) 是 true。而 indexOf 使用严格相等,NaN === NaN 是 false,所以 [NaN].indexOf(NaN) 是 -1。除此之外两者对 +0 和 -0 都视为相等,对象都按引用比较。还有一个差异在稀疏数组上:includes 会把空位当成 undefined 参与比较,indexOf 会直接跳过空位。

  • includes 用 SameValueZero,NaN 等于自身
  • indexOf 用 ===,永远找不到 NaN
  • 两者都认为 +0 和 -0 相等
  • 稀疏数组里 includes 把空位当 undefined,而 indexOf 跳过空位

同一个数组,两种结果

把 NaN、-0 和空位放进同一个例子里对比。

NaN、-0 和空位JavaScript
const arr = [1, NaN, -0]
console.log(arr.includes(NaN), arr.indexOf(NaN)) // SameValueZero 认 NaN,=== 不认
console.log(arr.includes(0), arr.indexOf(0)) // 两者都认为 0 和 -0 相等

const sparse = [1, , 3] // 下标 1 是空位
console.log(sparse.includes(undefined), sparse.indexOf(undefined))
查看输出与解释
true -1
true 2
true -1

第一行是题目的核心。NaN 表示「不是一个有效数字」,IEEE 754 浮点标准规定它不等于任何值,包括它自己,=== 遵守了这条规则。includes 是 ES2016 才加的,设计时专门处理了这个问题,因为「数组里有没有 NaN」是一个合理的需求。 第三行是空位的差异。includes 从 0 到 length - 1 逐个读值,空位读出来就是 undefined,所以能匹配。indexOf 是更早的方法,它会先检查这个下标上有没有属性,没有就跳过,所以找不到。

三种相等算法放在一起看

JavaScript 里其实有好几套相等规则,不同的 API 用的不一样,记住它们在 NaN 和 ±0 上的差别就够了。

===、Object.is 和 SetJavaScript
console.log(NaN === NaN, Object.is(NaN, NaN))
console.log(0 === -0, Object.is(0, -0))
console.log(new Set([NaN, NaN, 0, -0]).size) // Set 用 SameValueZero
console.log([NaN].findIndex(Number.isNaN)) // 找 NaN 的下标
查看输出与解释
false true
true false
2
0

Object.is 最严格,能区分 +0 和 -0。SameValueZero 介于两者之间:NaN 像 Object.is 那样认自己,±0 像 === 那样当成一个。Set 和 Map 的键也用这个规则,所以上面的 Set 里只剩一个 NaN 和一个 0。 需要 NaN 的下标时,indexOf 做不到,要用 findIndex(Number.isNaN)。注意不要传全局的 isNaN,它会先把参数转成数字,isNaN('abc') 也是 true。

回答前,多想一步

容易答错的地方

说 includes 用的是 Object.is
如果用的是 Object.is,那么 [-0].includes(0) 就该是 false,但实际是 true。所以 includes 用的是 SameValueZero,名字里的 Zero 就是指它不区分正负零。
以为 includes 能按内容找对象
[{ id: 1 }].includes({ id: 1 }) 是 false。三种相等算法对对象都只比较引用。按内容找要用 some 或 findIndex,自己写判断条件。
以为 includes 只是 indexOf !== -1 的简写
大多数时候两者可以互换,但 NaN 和稀疏数组这两种情况结果不同。代码里只关心「有没有」时,直接用 includes,意图更清楚,也不会被这两种边界坑到。
试着用自己的话回答

面试官还会怎么问?

字符串的 includes 和数组的 includes 一样吗?

不一样,它们是两个方法。字符串的 includes 查找子串,'abc'.includes('bc') 是 true,而且参数会被转成字符串。数组的 includes 比较的是元素,['abc'].includes('bc') 是 false。

两个方法都有第二个参数,含义一样吗?

一样,都是 fromIndex,表示从哪个下标开始找,负数表示从末尾往前数。比如 [1, 2, 1].includes(1, 1) 从下标 1 开始找,结果还是 true。

老浏览器不支持 includes,怎么兼容 NaN 的情况?

可以用 arr.some((x) => x === target || (x !== x && target !== target))。其中 x !== x 只有在 x 是 NaN 时才成立,这是 Number.isNaN 出现之前判断 NaN 的经典写法。不过如今直接引入 core-js 的 polyfill 更省事。

从一道题,走向一组知识

把知识连起来

数组与集合

Set 是怎么判断重复的,为什么 NaN 能去重而两个相同的对象不行?

Set 去重和 includes 用的是同一种比较,所以 NaN 能被去重,+0 和 -0 会被当成同一个。

数组与集合

找数组元素的下标,什么时候用 findIndex,什么时候用 indexOf?

需要 NaN 的下标时,indexOf 帮不上忙,要用 findIndex 配合 Number.isNaN。

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

参考资料

  • Array.prototype.includes()
  • 相等比较和相同

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

本题目录
  1. 先记住这个答案
  2. 同一个数组,两种结果
  3. 三种相等算法放在一起看
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

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

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