前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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 热点
旧版
首页程序员面试题库Object.is -0 NaN 比较
JavaScript值与类型

Object.is 和 === 有什么区别?什么时候非用 Object.is 不可?

除了两个特殊值,Object.is 和 === 的结果完全一样。NaN === NaN 是 false,Object.is(NaN, NaN) 是 true;0 === -0 是 true,Object.is(0, -0) 是 false。需要区分 -0,或者要一个「自己等于自己」的比较时才用 Object.is。

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

四种相等判断

  1. == 和 ===宽松相等会做类型转换,严格相等不转;两者都认为 NaN 不等于任何值,0 等于 -0
  2. SameValueZeroincludes、Set、Map 使用,NaN 等于 NaN,0 等于 -0
  3. SameValue即 Object.is,NaN 等于 NaN,0 不等于 -0,最「字面」的相等

对象比较时四种算法都只看引用,两个内容一样的对象用哪种都是不相等。Object.is 不是深比较。

核心回答

先记住这个答案

Object.is(a, b) 和 a === b 都不做类型转换,结果在绝大多数情况下一样,只有两处不同。第一,NaN === NaN 为 false,Object.is(NaN, NaN) 为 true。第二,0 === -0 为 true,Object.is(0, -0) 为 false。所以当你需要识别 -0,或者需要一个满足「值等于自身」的比较(比如判断状态有没有变化)时,才用 Object.is。另外还有第三种相等 SameValueZero,被 includes、Set、Map 使用,它在 NaN 上像 Object.is,在 -0 上像 ===。

  • Object.is 认为 NaN 等于 NaN
  • Object.is 认为 0 和 -0 不相等
  • SameValueZero 认为 NaN 与自身相等
  • React 使用 Object.is 比较依赖值

把差异摆在一起看

用几组值分别跑一遍 === 和 Object.is,再顺便看看数组和 Set 用的是哪种规则。

对照结果JavaScript
const pairs = [[NaN, NaN], [0, -0], [1, 1], ['1', 1]]
for (const [a, b] of pairs) {
  const show = (v) => (Object.is(v, -0) ? '-0' : typeof v === 'string' ? JSON.stringify(v) : String(v))
  console.log(show(a), 'vs', show(b), '| ===', a === b, '| Object.is', Object.is(a, b))
}
console.log([NaN].indexOf(NaN), [NaN].includes(NaN)) // -1 true
console.log(new Set([0, -0, NaN, NaN]).size) // 2
查看输出与解释
NaN vs NaN | === false | Object.is true
0 vs -0 | === true | Object.is false
1 vs 1 | === true | Object.is true
"1" vs 1 | === false | Object.is false
-1 true
2

代码里要自己写 show 函数,是因为 String(-0) 会输出 '0',直接打印看不出符号。这也说明 -0 平时很难被察觉。 后两行最实用。indexOf 内部用 ===,所以永远找不到 NaN;includes 用 SameValueZero,能找到。Set 同样用 SameValueZero,0 和 -0 被当成同一个元素,两个 NaN 也被合并,最后只剩两个成员。

-0 从哪来,为什么要区分

JavaScript 的数字是 IEEE 754 双精度浮点数,符号单独占一位,所以 0 有正负两种。大部分运算里两者表现一样,但有些地方符号会暴露出来。

-0 露出马脚的地方JavaScript
const x = -0
console.log(x === 0, String(x)) // 看起来就是 0
console.log(1 / x) // -Infinity,符号还在
console.log(Math.round(-0.4), Object.is(Math.round(-0.4), -0))
console.log(JSON.stringify(-0), Object.is(-0 + 0, 0))
查看输出与解释
true 0
-Infinity
0 true
0 true

Math.round(-0.4) 的结果是 -0,负数乘以 0、除以负无穷也会得到它。打印和 JSON.stringify 都把它显示成 0,但拿它做除数就会得到 -Infinity。在做方向相关的计算时,比如拖拽速度、坐标系里的朝向,符号有意义,这时就要用 Object.is(v, -0) 来判断。在 Object.is 出现之前,只能用 1 / v === -Infinity 这种绕弯的写法。

React 为什么用 Object.is
React 判断 setState 传入的新值和旧值是否相同、useEffect 的依赖有没有变化,用的都是 Object.is。原因在于 NaN:如果用 ===,一个值为 NaN 的 state 每次比较都「不相等」,会导致不必要的重新渲染,甚至死循环。Object.is 保证了同一个值一定等于自己。
回答前,多想一步

容易答错的地方

以为 Object.is 能比较对象内容
Object.is({}, {}) 是 false。它和 === 一样只比较引用,不做深比较。React 里直接修改对象再 setState 不会触发更新,就是因为引用没变,Object.is 返回了 true。
以为 includes 和 Object.is 规则一样
[-0].includes(0) 是 true,Object.is(-0, 0) 是 false。includes 用的是 SameValueZero,只在 NaN 上和 Object.is 一致,在 ±0 上和 === 一致。
判断 NaN 只会用 Object.is
判断某个值是不是 NaN,最直接的写法是 Number.isNaN(v)。全局的 isNaN 会先把参数转成数字,isNaN('abc') 也是 true,不能用来做严格判断。
试着用自己的话回答

面试官还会怎么问?

怎么手写 Object.is 的 polyfill?

分两步处理特殊情况。如果 a === b,还要排除 0 和 -0,写成 a !== 0 || 1 / a === 1 / b;如果 a !== b,只有两边都是 NaN 时才返回 true,写成 a !== a && b !== b,利用的正是 NaN 不等于自身。

Map 的 key 用 -0 会怎样?

Map 用 SameValueZero 比较 key,而且在存入时会把 -0 规范成 +0。所以 map.set(-0, 'a') 之后,map.get(0) 能取到 'a',遍历 key 时看到的也是 +0。

什么场景下应该默认用 Object.is 代替 ===?

一般不需要。业务代码里绝大多数比较不会碰到 NaN 和 -0,=== 更短、团队也更熟悉。需要写通用的「值是否改变」逻辑时,比如自己实现一个状态库、缓存或 memo 工具,才推荐用 Object.is,行为和 React 保持一致。

从一道题,走向一组知识

把知识连起来

值与类型

字符串和数字、布尔值用加号拼接时,到底是怎么转换的?

=== 不做类型转换,'1' + 1 却会把数字转成字符串,两题一起看能分清哪些运算会触发隐式转换。

值与类型

字符串里怎么用转义写 Unicode 字符?反斜杠本身又该怎么写?

同样是字符串比较的前置知识:'\u0041' === 'A' 为真,因为转义在解析阶段就变成了同一个字符。

参考资料

  • JavaScript:值与类型参考资料

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

本题目录
  1. 先记住这个答案
  2. 把差异摆在一起看
  3. -0 从哪来,为什么要区分
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

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

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