比较算法,常用的相等、全等比较
还有 ES6 新增的 Object.is 的同值比较
除此之外数组 includes 方法、Map set 方法 key 值相等使用的零值相等 (opens new window)比较
# 对比几种比较
三方面:+0、-0、NaN 与 NaN、是否发生隐式类型转换
--- | == | === | Object.is | SameValueZero |
---|---|---|---|---|
+0、-0 | true | true | false | true |
NaN、NaN | false | false | true | true |
隐藏类型转换 | 是 | 否 | 否 | 否 |
# 细聊相等比较(==)类型转换规则
两个操作数,x、y
- x、y 类型相同,与全等比较结果相同,特殊的 x、y 是 +0、-0 结果 true;是 NaN 与 NaN 结果 false
- x、y 其中一个是 null 或 undefined,另外一个必须是 null、undefined 其中一个,结果 true,否则 false
- x、y 其中一个是 object 类型,比如数组、object 等,将 object 按如下转换为原始类型后,再比较
- 如果 object 上部署了 [Symbol.toPrimitvie] 方法则调用这个方法
- 如果没有部署,调用 toString 方法
- x、y 都为原始类型且类型不同时转换如下:
- 其中一个操作数是 symbol,另外一个不是,结果 false
- 其中一个是 boolean,转换 boolean 为 number, true 为 1,false 为 0 再比较
- number 与 string 比较,转换 string 为 number,转换失败返回 NaN,比较结果是 false
- number 与 bigint 比较,比较他们的数字部分,如果 number 操作数是 +Infinity、-Infinity、NaN 结果 false
- string 与 bigint 比较,使用 BigInt() 转换 string,再比较
const obj = new String("0");
const big = 0n
const num = 0
obj == big // true
// obj 是 object,先转换为原始类型,由于没有 Symbol.toPrimitive 方法,调用 toString,结果字符串 "0"
// string 与 bigint 比较,将调用 BigInt("0"),结果 0n
// 0n 与 0n 比较,结果 true
obj == num // true
// obj 转换为字符串 "0"
// string 与 number 比较,string 转换成数字 0,结果 true
big == num // true
// number 与 bigint 比较,只比较数字部分,即 0 == 0,结果 true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 比较算法应用场景
# 相等比较
视具体的使用场景,前提是了解比较规则,正常开发规范中禁用相等比较,推荐使用全等比较
若结果可能是 null 或 undefined 时,可以使用相等比较,代码更加简洁
let value = null // 或 undefined
if(value == undefined) {
// code ...
}
1
2
3
4
2
3
4
# 全等比较
Array.prototype.indexOf()
Array.prototype.lastIndexOf()
TypedArray.prototype.index()
TypedArray.prototype.lastIndexOf()
String.prototype.indexOf()
String.prototype.lastIndexOf()
switch ... case ...
1
2
3
4
5
6
7
2
3
4
5
6
7
# 同值比较
Object.is
1
# 零值相等比较
Array.prototype.includes()
TypedArray.prototype.includes()
Map.set 方法以 +0、-0、NaN 为 key 值
1
2
3
4
5
2
3
4
5
# ECMA 标准中的比较规则
# Abstract Equality Comparison 比较算法规则
两个操作数 x、y
- x、y 类型相同,结果与
===
相同 - x 是 null,y 是 undefined,结果 true;相反,结果 true
- x 是 number,y 是 string,y 转换为 number 再比较;相反,转换 x 类型
- x 是 boolean,转换 x 为 number 再与 y 比较;相反,转换 y
- x 是 string、number 或 symbol,y 是 object,转换 y 为原始类型再比较;相反,转换 x 为原始类型
- 其他情况结果 false
# ECMA 的 SameValue 比较算法规则
两个操作数 x、y
- 如果 x 类型与 y 类型不同,结果 false
- 如果 x 类型是 number,那么
- 如果 x 是 NaN,y 是 NaN,结果 true
- 如果 x 是 +0,y 是 -0,结果 false
- 如果 x 是 -0,y 是 +0,结果 aflse
- 如果 x 与 number 类型的 y 值相等,结果 true
- 其他情况结果 false
- 其他情况遵循 SameValueNonNumber (opens new window) 算法:
- 断言:x 不是 number 类型, y 与 x 类型相同
- x 是 undefined,结果 true
- x 是 null,结果 true
- x 是 string,y 只有与 x 相同长度字顺序相同,结果 true,否则 false
- x 是 boolean,y 与 x 同为 true 或 false,结果 true,否则 false
- x 是 symbol,y 与 x 指向相同 symbol,结果 true,否则 false
- x 与 y 指向相同对象,结果 true,否则 falses
# 比较算法中英文对照
相等比较:isLooselyEqual,ECMA 标准是 Abstract Equality Comparison,即抽象相等比较
全等比较:isStrictEqual,ECMA 标准是 Strict Equality Comparison,即严格相等比较
同值比较:SameValue,依赖 SameValueNonNumber (opens new window)
零值相等比较:SameValueZero
# 加餐 lodash 的 isEqual 尝试比较
isEqual (opens new window) 深度比较
个人观点是结构化比较,只要结构相同,结果为 true
又有点像鸭式比较 ⚗️
比如,两个引用不同的对象,只要结构相同,结果为 true
const a = {name: 1}
const b = {name: 1}
isEqual(a, b) // true,相等、全等、还是 Object.is 结果都为 false
1
2
3
4
2
3
4
# 小结
相等、全等、同值、零值相等比较,只关注正负 0,NaN,是否发生隐式类型转换即可
另外就是不同的 API,使用的比较算法不同
比如:
- Object.is 使用同值比较
- [].includes 使用零值相等比较
- switch ... case 语句、[].indexOf 使用全等比较
相等比较视具体的业务场景决定是否使用,规则较多,大致如下几个方面:
- 类型相等,用全等
- object 转为原始类型,先调用 [Symbol.toPrimitive],没有调用 toString,目前只有Date 、 Symbol 部署了 [Symbol.toPrimitive] 方法
- 类型不同原始类型向 number 类型转换
扫一扫,微信中打开