引言
在上文梳理 Map 的过程中, 在键值相等性比较一节遇到 零值相等
概念, 当时对这个概念那是一知半解, 所以特地针对 相等性判断
相关知识做了一次详细的梳理
一、严格相等「===」
1.1 简述
严格相等运算符 ===
会检查它的两个操作数是否相等, 返回一个布尔值; 需要注意的是: 严格相等运算符总是认为不同类型的操作数是不同的
1.2 规则 & 演示
- 操作数的类型不同, 返回
false
'1' === 1 // false
1 === 1 // true
- 操作数都是对象, 只有当它们指向同一个对象时才返回
true
, 因为比较的是引用地址
const obj = {}
const obc = obj
obj === obc // true
const o = {}
const b = {}
o === b // false
- 字符串类型必须拥有相同顺序的相同字符
'123' === '321' // false
'123' === '123' // true
- 布尔运算符必须同时为
true
或同时为false
true === true // true
true === false // false
- 操作数都为
null
或都为undefined
, 返回true
null === null // true
undefined === undefined // true
- 操作数有任意一个为
NaN
, 返回false
NaN === '1' // false
NaN === NaN // false
- 数字类型必须拥有相同的数值, 同时需要注意:
+0
和-0
会被认为是相同的值
1 === 1 // true
0 === -0 // true
二、相等「==」
2.1 简述
- 相等运算符
==
检查其两个操作数是否相等, 返回一个布尔值结果 - 需要注意的是, 与
===
不同的是,==
在比较前会将两个被比较的值转换为相同类型后再使用===
的规则进行比较 - 也就是说
==
是存在隐式类型转换
的
2.2 规则 & 演示
下图演示的是使用 ==
来对各种类型的操作数进行比较时, 对应的运行结果以及数据转换逻辑:
ToNumber(A)
表示在比较前将参数A
转换为数字ToPrimitive(A)
表示在比较前尝试调用A.toString()
和A.valueOf()
方法, 获取到参数A
的原始值IsFalsy(A)
当且仅当A
为undefined
时返回true
- 相同类型的比较, 将遵循
===
规则进行比较
1 == 1; // true
"hello" == "hello"; // true
NaN === NaN // false
0 === -0 // true
- 有类型转换的比较
- 类型是
布尔值
、字符串
和数字
进行比较会优先转换为数字
, 进行比较
"1" == 1; // true
1 == "1"; // true
0 == false; // true
字符串
和布尔值
对比, 会统一转为数字
进行比较
'0' == true; // false
'1231' == true; // false
'ada' == true; // false
'1' == true; // true
null
和undefined
相等, 和其他任何类型数据对比都是false
null == undefined; // true
0 == null; // false
0 == undefined; // false
false == null; // false
在
==
运算中, 会尽可能将基本类型
数据转为数字
再进行比较
- 对象
- 两个操作数都是对象, 按照
===
进行比较, 比较的将是对象的引用地址
const object1 = {};
const object2 = {};
console.log(object1 == object2); // false
console.log(object1 == object1); // true
- 如果两个操作数有且仅有一个是对象, 则会先调用对象的
toString()
或valueOf()
获取到对象的原始值再进行比较
const a = [1, 2, 3];
const b = "1,2,3";
a == b; // true, a 调用了 toString() 方法, 再和 b 进行比较
a.toString() === b; // true
const string1 = "hello"; // 字符串类型
const string2 = String("hello"); // 字符串类型
const string3 = new String("hello"); // 对象
const string4 = new String("hello"); // 对象
console.log(string1 == string2); // true
console.log(string1 == string3); // true
console.log(string2 == string3); // true
console.log(string4 == string4); // true
console.log(string3 == string4); // false
三、同值相等 「Object.is()」
3.1 简述
JS 中 Object.is()
方法可以用于判断两个值是否为同一个值, 该方法判断逻辑和 ===
区别如下:
- 在
Object.is()
中认为不同的NaN
是同一个值 - 在
Object.is()
中认为+0
和-0
是不同的值 - 其余判断逻辑和
===
保持一致
NaN === NaN; // false
Object.is(NaN, NaN); // true
0 === -0; // true
Object.is(0, -0); // false
3.2 对象中「不可变属性」相等性判断
当我们为对象设置了一个不可变属性, 后面如果试图修改该属性值, 将使用 同值相等
算法来判断修改前后的值是否是同一个, 如果不是将会抛出错误
const obj = {}
Object.defineProperty(
obj,
"NaN",
{
value: NaN,
writable: false,
enumerable: false,
configurable: false,
}
);
// 同值相等算法认为不同 NaN 是同一个值, 所以这里不会报错
Object.defineProperty(obj, "NaN", { value: NaN });
Object.defineProperty(
obj,
"Number",
{
value: 0,
writable: false,
enumerable: false,
configurable: false,
}
);
// 同值相等算法认为不同 0 和 -0 不是同一个值, 所以这里将会报错
Object.defineProperty(obj, "Number", { value: -0 });
3.3 Polyfill: 手撸一个 「Object.is()」
如下代码演示了如何自己实现一个 Object.is()
, 其中关键判断有两点
- 在
JS
中我们可以通过x !== x
判断x
是否是NaN
- 我们可以通过
1 / x === 1 / y
来确保x
y
都是0
或者-0
, 因为1 / 0 === Infinity
1 / -0 === -Infinity
if (!Object.is) {
Object.defineProperty(Object, "is", {
value: function (x, y) {
if (x === y) {
// x y 不能 0 || 都等于 0 或者 -0
return x !== 0 || 1 / x === 1 / y;
} else {
// 处理 NaN
return x !== x && y !== y;
}
}
});
}
四、零值相等
与 同值相等
唯一不同的是, 零值相等
认为 0
和 -0
是相等的, JS
中 同值相等
是由 Object.is()
提供, 但是 零值相等
是没有提供任何直接判断的方法的
在 Map
和 Set
中对于键、值的比较就是使用 零值相等
算法来进行比较的:
const map = new Map()
map.set(0, '0')
map.set(NaN, '1')
map.has(-0) // true
map.has(NaN) // true
const set = new Set()
set.add(0)
set.add(NaN)
set.has(-0) // true
set.has(NaN) // true
五、总结
5.1 严格相等「===」
- 对比类型、对比值
NaN
认为是不同的值0
和-0
认为是相同的值
5.2 相等「==」
- 不同类型会先进行转换, 然后使用
===
规则进行对比 - 会尽可能将基本类型数据转为数字再进行比较, 所以
'1231' == true
和'ada' == true
都为false
null
和undefined
相等, 同时他们和其他任何值都不等
5.3 同值相等
NaN
被认为是相等的0
和-0
被认为是不等的- 其他规则同
===
JS
中由Object.is()
方法提供该算法
5.4 零值相等
- 和同值相等唯一区别是, 在这里
0
和-0
被认为是相等的 - 和
===
唯一区别是, 认为NaN
是相等的 - 目前该算法没有暴露出对应的接口, 只是在
Map
Set
中使用了该算法
5.5 对比图
下图是 ==
===
Object.is()
在不同类型数据下的结果
六、参考
本文正在参加「金石计划」
原文链接:https://juejin.cn/post/7217459018238705724 作者:墨渊君