众所周知,javascript是类型不严格的语言,因此在相等判断时,可能会出现因类型转换,以及不同比较规则导致的混淆。此处整理以记忆不同相等性判断方法在基本数据类型上的区别
相等性判断概览
Javascipt提供的常用相等判断api
==,===,Object.is
相等性判断抽象概念
- IsLooselyEqual:==
- IsStrictlyEqual:===
- SameValue
同值相等:Object.is() - SameValueZero
零值相等:被许多内置运算(涉及相等性判断处)使用
相等性判断方式分析
"=="
会进行隐式类型转换
但要注意的是 undefined 和 null 参与运算时不会类型转换
所以null、undefined和其他比较都返回false
但是undefined == null返回true
"==="
严格相等
但要注意NaN === NaN返回false
因此:
数组索引查找方法也使用严格相等,包括 Array.prototype.indexOf()、Array.prototype.lastIndexOf()、TypedArray.prototype.index()、TypedArray.prototype.lastIndexOf() 和 case 匹配。这意味着你不能使用 indexOf(NaN) 查找数组中 NaN 值的索引,也不能将 NaN 用作 case 值在 switch 语句中匹配任何内容。
"Object.is"(同值相等
同值相等的人柱力,同值相等决定了两个值在所有上下文中是否在功能上相同
可以用里氏替换原则的思路来理解
// 向 Nmuber 构造函数添加一个不可变的属性 NEGATIVE_ZERO
Object.defineProperty(Number, "NEGATIVE_ZERO", {
value: -0,
writable: false,
configurable: false,
enumerable: false,
});
// 调用以下函数会使用Object.is来比较变动
// attemptMutation(0)会弹出错误
function attemptMutation(v) {
Object.defineProperty(Number, "NEGATIVE_ZERO", { value: v });
}
1.对于+0 -0在不同上下文中的区别
- Number.parseInt("-0")会返回-0。
- 1/-0为-Infinity,而1/+0为+Infinity。
- 在JSON.stringify()中+0会输出为0,但-0会输出为-0。
- canvas渲染时+0和-0的颜色可能不同。
- +0内部是以一串二进制数"0x0000000000000"的形式存储;而-0内部是"0x8000000000000"
可能因此认为正负零不同值
2.对于NaN
即Number.NaN,Object.is认为NaN的值都是相同的全局对象属性,因此认为其是同值
零值相等
基于同值相等,零值变为相等
零值相等与严格相等的区别在于其将 NaN 视作是相等的,与同值相等的区别在于其将 -0 和 0 视作相等的。这使得它在搜索期间通常具有最实用的行为,特别是在与 NaN 一起使用时。它被用于 Array.prototype.includes()、TypedArray.prototype.includes() 及 Map 和 Set 方法用来比较键的相等性。
区别和联系
==和===代码中最常见的相等判断,区分在于会不会做隐式转换
Object.is和同值相等Object.is是实现同值相等的方法,同值相等的载体是Object.is
同值相等和零值相等是一对区别的概念
记忆法
首先记住特殊的null == undefined 结果为 true
然后记住类型转换的方法和引用类型的判断
之后特殊的区别集中在
| x | y | === | Object.is | SameValueZero |
|---|---|---|---|---|
+0 | -0 | ✅ true | ❌ false | ✅ true |
0 | -0 | ✅ true | ❌ false | ✅ true |
NaN | NaN | ❌ false | ✅ true | ✅ true |
Object.is,也即同值相等是最原始的比较方式,因此在NaN和+-0的比较上不做特殊处理
===是特化数字比较的Object.is,处理了遇到NaN和+-零的情况,使其符合相等比较的使用习惯,NaN因包含了不确定的非数被认为是不相等的,+-0在相等比较的语境下不需要做区别
==是特化类型转换的===
零值相等是特化搜索的同值相等,在搜索中,我们关注的重点不在0的+-上,因此两者相等
比较结果的对比
运用刚刚的知识来验证一下这个表格吧!
| x | y | == | === | Object.is | SameValueZero |
|---|---|---|---|---|---|
undefined | undefined | ✅ true | ✅ true | ✅ true | ✅ true |
null | null | ✅ true | ✅ true | ✅ true | ✅ true |
true | true | ✅ true | ✅ true | ✅ true | ✅ true |
false | false | ✅ true | ✅ true | ✅ true | ✅ true |
'foo' | 'foo' | ✅ true | ✅ true | ✅ true | ✅ true |
0 | 0 | ✅ true | ✅ true | ✅ true | ✅ true |
+0 | -0 | ✅ true | ✅ true | ❌ false | ✅ true |
+0 | 0 | ✅ true | ✅ true | ✅ true | ✅ true |
-0 | 0 | ✅ true | ✅ true | ❌ false | ✅ true |
0n | -0n | ✅ true | ✅ true | ✅ true | ✅ true |
0 | false | ✅ true | ❌ false | ❌ false | ❌ false |
"" | false | ✅ true | ❌ false | ❌ false | ❌ false |
"" | 0 | ✅ true | ❌ false | ❌ false | ❌ false |
'0' | 0 | ✅ true | ❌ false | ❌ false | ❌ false |
'17' | 17 | ✅ true | ❌ false | ❌ false | ❌ false |
[1, 2] | '1,2' | ✅ true | ❌ false | ❌ false | ❌ false |
new String('foo') | 'foo' | ✅ true | ❌ false | ❌ false | ❌ false |
null | undefined | ✅ true | ❌ false | ❌ false | ❌ false |
null | false | ❌ false | ❌ false | ❌ false | ❌ false |
undefined | false | ❌ false | ❌ false | ❌ false | ❌ false |
{ foo: 'bar' } | { foo: 'bar' } | ❌ false | ❌ false | ❌ false | ❌ false |
new String('foo') | new String('foo') | ❌ false | ❌ false | ❌ false | ❌ false |
0 | null | ❌ false | ❌ false | ❌ false | ❌ false |
0 | NaN | ❌ false | ❌ false | ❌ false | ❌ false |
'foo' | NaN | ❌ false | ❌ false | ❌ false | ❌ false |
NaN | NaN | ❌ false | ❌ false | ✅ true | ✅ true |