Object.is() 是什么?和 === 有什么区别?
Object.is不做类型转换,采用 SameValue 语义:它认为NaN等于自身,并区分+0与-0
先给出面试版结论:Object.is(a, b) 用于判断两个值是否是“同一个值”。它不会进行隐式类型转换;对于对象仍然比较引用。它和 === 只有两个关键差异:Object.is(NaN, NaN) 是 true,Object.is(+0, -0) 是 false。
1. 基本用法
返回一个布尔值。它不会把字符串转成数字,也不会把对象转成原始值:
对于大多数普通值,Object.is 和 === 的结果一致:
它虽然挂在 Object 上,但参数不必是对象,也不会对参数进行装箱后再比较。
2. 与 ==、=== 的完整对比
因此不能简单说 Object.is 是“更严格的 ===”。它采用的是另一套相等语义:对 NaN 更宽松,对正负零更严格。
3. 为什么 NaN 能等于自身?
NaN 表示某次数值运算没有得到有效数值。按照 IEEE 754 和 JavaScript 的相等规则,使用 == 或 === 时,NaN 不等于任何值,包括它自己:
SameValue 语义特意把两个 NaN 视为相同,所以 Object.is 可以判断一个值是不是 NaN:
不过表达业务意图时,通常优先使用 Number.isNaN:
不要使用全局 isNaN 代替它,因为全局 isNaN 会先进行数值转换:
4. 为什么要区分 +0 和 -0?
JavaScript 的 Number 使用 IEEE 754 双精度浮点数,零也带有符号位,因此存在 +0 和 -0。它们在大多数运算中表现相同,但并非完全不可区分:
负零可以来自保留方向信息的计算:
在绝大多数业务场景中不需要区分正负零;数值算法、坐标方向、符号传播等场景才可能需要 SameValue 的这项特性。
判断负零可以写成:
5. 对象仍然比较引用
Object.is 不会深度比较对象内容:
两个对象字面量会创建两个不同对象,即使属性完全相同,也不是同一个引用。只有两边指向同一个对象时才返回 true:
完整链路是:
如果业务需要比较对象结构,应根据数据模型逐字段比较,或选择适合的深比较策略;Object.is 不负责这件事。
6. SameValue、Strict Equality 和 SameValueZero
JavaScript 不只有一套“相等”算法。常见的三套语义是:
SameValueZero 可以理解为:像 Object.is 一样识别 NaN,但不区分正负零。
这解释了为什么不同 API 面对相同数据可能给出不同结果。回答这类题时,不要只说“底层用了 Object.is”;更准确的做法是指出具体 API 采用哪种相等算法。
7. 一个最小实现
下面是 Object.is 的常见等价实现,用于理解两个特殊分支:
它的执行过程是:
- 先用
===处理绝大多数情况; - 如果两者严格相等且为零,再比较倒数,区分
Infinity和-Infinity; - 如果不严格相等,检查两边是否都具有
NaN的“不等于自身”特征。
这是符合常见 polyfill 思路的教学实现,不是在复刻某个 JavaScript 引擎的内部源码。现代引擎可能直接使用内部类型标签、浮点位或优化后的指令完成判断。
8. Object.is 在 React 中的作用
React 的多处比较语义与 Object.is 有关。例如 Hooks 的依赖项会逐项用 Object.is 比较:
可以简化理解为:
这会产生两个容易忽略的边界:
useMemo、useCallback 的依赖比较也采用相同思路。React.memo 默认对每个 prop 使用 Object.is 语义进行浅比较,所以新对象、新数组和新函数仍会被视为变化。
这里的重点不是背“React 用了 Object.is”,而是理解它只比较一层引用:
9. 边界与常见陷阱
陷阱一:以为它会深比较
Object.is 比较对象身份,不遍历属性。
陷阱二:用它比较浮点计算误差
它不会修复 IEEE 754 精度问题。浮点近似比较通常要结合业务量级设置容差:
固定使用 Number.EPSILON 也不适合所有数量级,容差必须由业务精度要求决定。
陷阱三:认为它会发生类型转换
包装对象是对象,原始数字是原始值;两者不是同一个值。
陷阱四:混淆 isNaN 和 Object.is
Object.is(value, NaN) 可以准确识别 NaN,但 Number.isNaN(value) 意图更清晰。全局 isNaN 会进行类型转换,语义不同。
陷阱五:认为 includes 内部就是调用 Object.is
二者在 NaN 上表现相同,但 includes 使用 SameValueZero,不区分 +0 和 -0,不能直接画等号。
陷阱六:看到 Object.is 就替换所有 ===
大多数业务比较用 === 更符合直觉,也更常见。只有明确需要把 NaN 当作相同值、区分正负零,或需要遵循某个 API 的 SameValue 语义时,才有必要主动选择 Object.is。
10. 面试官递进追问
1. Object.is 是做什么的?
它使用 SameValue 语义比较两个值,不做类型转换;对象比较引用。与 === 的差异只有 NaN 和正负零两组边界。
为什么问: 检查基础定义。回答要先给共同点,再给两个差异。
2. Object.is 是否比 === 更严格?
不宜这样概括。它对正负零区分得更细,但把两个 NaN 视为相同,因此是不同语义,而不是单向“更严格”。
为什么问: 检查是否只会背口号。回答应抓住两个方向相反的差异。
3. Object.is({}, {}) 为什么是 false?
两个对象字面量创建了不同对象。SameValue 对对象比较身份或引用,不比较属性结构。
为什么问: 检查是否混淆同值比较与深比较。
4. 如何不用 Object.is 区分 +0 和 -0?
先确认值等于零,再比较倒数:1 / +0 是 Infinity,1 / -0 是 -Infinity。
为什么问: 检查是否理解实现中的特殊分支,而不只是记输出。
5. Number.isNaN 和 Object.is(value, NaN) 有什么区别?
判断单个值是否为 NaN 时,结果基本一致且都不做字符串到数字的强制转换;Number.isNaN 表意更直接。Object.is 是通用的两值 SameValue 比较。
为什么问: 检查 API 选择和代码可读性意识。
6. includes 为什么能找到 NaN,indexOf 却不能?
includes 使用 SameValueZero,认为 NaN 与自身相等;indexOf 使用 Strict Equality,NaN === NaN 为 false。
为什么问: 检查能否把知识迁移到标准库,而不是误以为所有比较都用 ===。
7. React 为什么会受 Object.is 的对象引用规则影响?
Hook 依赖和默认 memo props 比较只看每个值是否 SameValue。每次 render 创建的新对象或函数引用不同,即使内容看起来一样,也会被判定为变化。
为什么问: 检查语言基础与框架机制能否连起来。
8. Object.is 能解决 0.1 + 0.2 的问题吗?
不能。它只定义如何比较已有值,不改变浮点数的表示和计算。近似比较需要按业务精度设计容差,金额等精确十进制场景应选择整数最小单位或十进制库。
为什么问: 检查是否混淆相等算法与数值精度。
9. 什么时候应该在项目中主动使用它?
需要准确识别 NaN、区分正负零、实现 SameValue 语义的工具,或解释 React 等 API 的变化判断时使用。普通业务相等判断通常仍优先 ===。
为什么问: 检查能否把理论转成工程决策,并考虑可读性。
11. 常见错误回答
- “
Object.is就是严格版===。” 不准确,因为它对NaN反而给出相等结果。应说两者采用不同相等语义。 - “
Object.is可以比较对象内容。” 错误。对象仍按引用比较。 - “
Object.is不会出现浮点误差。” 错误。它不参与数值计算,只比较计算后的值。 - “
Object.is判断NaN时等同于全局isNaN。” 错误。全局isNaN会先做类型转换。 - “
Set和includes都使用Object.is。” 不严谨。它们使用 SameValueZero,不区分正负零。 - “React 依赖比较会深度检查对象。” 错误。对象引用变化就会被视为依赖变化。
12. 最终记忆框架
核心关键词:SameValue、不做类型转换、NaN、正负零、对象引用。
一句话本质:Object.is 是 JavaScript 的 SameValue 比较入口,主要修正 === 对 NaN 和正负零的两项特殊语义。
一分钟回答:说明不做类型转换、对象比较引用,以及与 === 的两个差异。
三分钟回答:补充 SameValue、Strict Equality、SameValueZero 的对比,以及 includes、Set 和 React 的典型应用。
十分钟回答:从教学版实现讲到 NaN、IEEE 754 正负零、对象身份、React Hook 依赖比较、浮点误差与深比较的边界,再说明工程中何时选择 === 或 Object.is。
这套理解还可以迁移到 Map/Set 去重、数组查找、React 依赖数组、不可变数据、引用稳定性和浮点数精度问题。面试时始终先问:当前 API 采用哪种相等算法?它是否转换类型、如何处理 NaN 和正负零、对象按值还是按引用?

