JavaScript 四种相等算法详解:同值、同值零、非严格与严格相等
本文详解 JavaScript 中的四种相等算法:同值、同值零、非严格相等(==)与严格相等(===)。重点解析非严格相等的隐式类型转换规则、严格相等对 NaN 和 ±0 的处理差异,以及 Object.is 和 includes 等方法的底层逻辑,帮助开发者避免隐式转换陷阱,选择正确的比较方式。
前言
在 JavaScript 中判断两个值是否相等,看似基础却暗藏玄机。JavaScript 规范中定义了四种不同的相等算法,若不清楚它们的具体区别,容易在开发中遇到难以排查的问题。本文旨在深入解析这四种算法,帮助读者建立正确的比较观念。
ECMAScript 作为 JavaScript 的核心规范,明确定义了四种相等算法,具体分类如下图所示:

上述图片展示了四种算法的中文名称。前端开发者通常对“严格相等”和“非严格相等”较为熟悉,但对于“同值”和“同值零”可能较为陌生。接下来将逐一介绍这四种算法的具体行为与应用场景。
- 同值
- 同值零
- 非严格相等
- 严格相等
非严格相等
非严格相等使用双等号(==)进行比较,其核心语义是“语义相等”,不要求操作数类型一致。在比较前,JavaScript 引擎会尝试将两个操作数的类型转换为一致,然后再进行值比较。代码示例如下:
1 == 1; // true
1 == '1'; // true 类型不同,不影响比较结果
非严格相等的类型转换规则极为复杂,难以凭记忆掌握。社区中有开发者将其整理为可视化图表,如下图所示,直观展示了各种类型间的转换路径:

为了便于记忆非对象类型的转换逻辑,可以总结为以下三条核心规则:
- Undefined 类型仅与 Null 类型相等
- 当与 Number 类型比较时,另一个值会自动转换为 Number 类型
- 当与 Boolean 类型比较时,另一个值会先转换为 Number 类型
若比较值为对象,JavaScript 会调用其内部的 ToPrimitive 方法将其转换为原始值。开发者可以通过定义 Symbol.toPrimitive 符号来自定义转换行为。需要注意的是,在非严格相等判断中,Symbol.toPrimitive 接收的 hint 参数始终为
const obj = {
[Symbol.toPrimitive](hint) {
console.log(hint);
if (hint == 'number') {
return 1;
}
if (hint == 'string') {
return 'yan';
}
return true;
},
};
console.log(obj == 1); // obj 返回 true
console.log(obj == '1'); // obj 返回 true
console.log(obj == true); // obj 返回 true
非严格相等虽然通过隐式自动转换简化了部分场景(如 Number 与 String 的转换,便于处理表单或 URL 参数),但其带来的隐式转换问题往往多于便利。
由于隐式转换规则复杂且难以预测,主流观点已不推荐广泛使用非严格相等。作者建议,仅在明确需要判断 undefined 和 null 的场景下,才使用非严格相等。
严格相等
严格相等是另一种比较算法,其与非严格相等的核心区别在于不进行任何类型转换。如果两个操作数的类型不一致,直接返回 false。严格相等对应 === 操作符,因使用三个等号,也被称为“三等”或“全等”。代码示例如下:
1 === 1; // true
1 === '1'; // false 类型不同,影响比较结果
不同类型值的判断规则如下,与非严格相等相比,严格相等的行为更符合直觉,避免了意外的类型转换。

严格相等解决了非严格相等中隐式转换带来的隐患,但也失去了隐式转换的便利。对于类型可能不一致的场景(例如从表单获取的值均为字符串),保险的做法是在比较前进行手动类型转换。代码示例如下:
1 === Number('1'); // true 手动类型转换,类型防御
虽然严格相等在绝大多数情况下是正确的选择,但仍存在例外情况,主要涉及 NaN 和正负零(+0/-0)的处理。
Number 类型有一个特殊值 NaN,用于表示计算错误。常见于非 Number 类型与 Number 类型运算时,结果可能为 NaN,这是从表单或接口获取数据时容易遇到的问题。代码示例如下所示:
const a = 0 / 0; // NaN
const b = 'a' / 1;
const c = undefined + 1; // NaN
在严格相等中,NaN 不等于自身,即 (x !== x) 是唯一成立的情况。若需判断 NaN,可使用 isNaN 函数。ECMAScript 2015 引入了更语义化的 Number.isNaN,它不会对参数进行类型转换,建议优先使用,但需注意浏览器兼容性问题。判断 NaN 的代码示例如下:
NaN === NaN; // false
isNaN(NaN); // true
Number.isNaN(NaN); // true
isNaN('aaa'); // true 自动转换类型 'aaa'转换为Number为NaN
Number.isNaN('aaa'); // false 不进行转换,类型不为Number,直接返回false
严格相等的另一个例外是无法区分 +0 和 -0,代码示例如下。在某些数学计算场景中,区分正负零具有语义重要性。
+0 === -0; // true
JavaScript 中许多系统函数使用严格相等,如数组的 indexOf、lastIndexOf 以及 switch-case 语句。需要注意的是,这些方法无法正确识别 NaN,代码示例如下:
[NaN].indexOf(NaN); // -1 数组中其实存在NaN
[NaN].lastIndexOf(NaN); // -1
同值零
同值零(SameValueZero)是另一种相等算法,名称源自规范直译。其功能与严格相等基本一致,唯一区别在于对 NaN 的处理:同值零认为 NaN 等于 NaN。这在判断 NaN 是否存在于集合中的语义下更为合理。
ECMAScript 2016 引入的 Array.prototype.includes 方法使用同值零算法。此外,Map 的键去重和 Set 的值去重也采用此算法。代码示例如下:
[NaN].incdudes(NaN); // true 注意和indexOf的区别,incdudes的语义更合理
new Set([NaN, NaN]); // [NaN] set中只会有个一个NaN,如果 NaN !== NaN的话,应该是[NaN, NaN]
new Map([
[NaN, 1],
[NaN, 2],
]); // {NaN => 2} 如果 NaN !== NaN的话,应该是 {NaN => 1, NaN => 2}
同值
同值(SameValue)是最后一种相等算法,与同值零类似,但认为 +0 不等于 -0。ECMAScript 2015 引入的 Object.is 方法使用同值算法。代码示例如下:
Object.is(NaN, NaN); // true
Object.is(+0, -0); // false 📢 注意这里
同值算法适用于确定两个值在“任何情况下功能上是否相同”的场景,使用频率较低。例如,Object.defineProperty 使用同值算法确认键是否存在:将存在的只读属性值 -0 修改为 +0 时会报错,而设置为同样的 -0 则正常执行。代码示例如下:
function test() {
'use strict'; // 需要开启严格模式
var a = {};
Object.defineProperty(a, 'a1', {
value: -0,
writable: false,
configurable: false,
enumerable: false,
});
Object.defineProperty(a, 'a1', {
value: -0,
}); // 正常执行
Object.defineProperty(a, 'a1', {
value: 0,
}); // Uncaught TypeError: Cannot redefine property: a1
}
test();
在判断数组中是否存在特定值的场景中,若需区分 +0 和 -0,可使用 ECMAScript 2015 引入的 Array.prototype.find 方法,并自行控制判断逻辑。代码示例如下:
[0].includes(-0); // 不能区分-0
[0].find((val) => Object.is(val, -0)); // 能区分+0和-0
总结
最后,通过下表对比四种算法的区别:
| 隐式转换 | NaN 和 NaN | +0 和 -0 | |
|---|---|---|---|
| 非严格相等(==) | 是 | false | true |
| 严格相等(===) | 否 | false | true |
| 同值零(includes 等) | 否 | true | true |
| 同值(Object.is) | 否 | true | false |
到此这篇关于 JavaScript 判断两个值相等的方法详解的文章就介绍到这了,更多相关 JavaScript 判断值相等内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!
JS中使用位运算(~~)和一元加号(+)快速转换字符串为整数
2026-11-03 22:41:12
怎么使用命令行批量格式化项目中的JS文件
2026-11-03 19:04:42
JS代码美化后报错怎么办?常见格式化冲突解决方法
2026-11-03 18:47:01
不用moment.js,怎么用原生JS Intl API格式化时间戳
2026-11-03 18:10:30
原生JS与dayjs获取当前日期的代码对比
2026-11-03 17:57:26
