比较算法

比较算法,常用的相等、全等比较

还有 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

  1. x、y 类型相同,与全等比较结果相同,特殊的 x、y 是 +0、-0 结果 true;是 NaN 与 NaN 结果 false
  2. x、y 其中一个是 null 或 undefined,另外一个必须是 null、undefined 其中一个,结果 true,否则 false
  3. x、y 其中一个是 object 类型,比如数组、object 等,将 object 按如下转换为原始类型后,再比较
    1. 如果 object 上部署了 [Symbol.toPrimitvie] 方法则调用这个方法
    2. 如果没有部署,调用 toString 方法
  4. x、y 都为原始类型且类型不同时转换如下:
    1. 其中一个操作数是 symbol,另外一个不是,结果 false
    2. 其中一个是 boolean,转换 boolean 为 number, true 为 1,false 为 0 再比较
    3. number 与 string 比较,转换 string 为 number,转换失败返回 NaN,比较结果是 false
    4. number 与 bigint 比较,比较他们的数字部分,如果 number 操作数是 +Infinity、-Infinity、NaN 结果 false
    5. 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

# 比较算法应用场景

# 相等比较

视具体的使用场景,前提是了解比较规则,正常开发规范中禁用相等比较,推荐使用全等比较

若结果可能是 null 或 undefined 时,可以使用相等比较,代码更加简洁

let value = null // 或 undefined
if(value == undefined) {
  // code ...
}
1
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

# 同值比较

Object.is
1

# 零值相等比较

Array.prototype.includes()

TypedArray.prototype.includes()

Map.set 方法以 +0-0NaN 为 key 值
1
2
3
4
5

# ECMA 标准中的比较规则

# Abstract Equality Comparison 比较算法规则

两个操作数 x、y

  1. x、y 类型相同,结果与 === 相同
  2. x 是 null,y 是 undefined,结果 true;相反,结果 true
  3. x 是 number,y 是 string,y 转换为 number 再比较;相反,转换 x 类型
  4. x 是 boolean,转换 x 为 number 再与 y 比较;相反,转换 y
  5. x 是 string、number 或 symbol,y 是 object,转换 y 为原始类型再比较;相反,转换 x 为原始类型
  6. 其他情况结果 false

# ECMA 的 SameValue 比较算法规则

两个操作数 x、y

  1. 如果 x 类型与 y 类型不同,结果 false
  2. 如果 x 类型是 number,那么
  3. 如果 x 是 NaN,y 是 NaN,结果 true
  4. 如果 x 是 +0,y 是 -0,结果 false
  5. 如果 x 是 -0,y 是 +0,结果 aflse
  6. 如果 x 与 number 类型的 y 值相等,结果 true
  7. 其他情况结果 false
  8. 其他情况遵循 SameValueNonNumber (opens new window) 算法:
  9. 断言:x 不是 number 类型, y 与 x 类型相同
  10. x 是 undefined,结果 true
  11. x 是 null,结果 true
  12. x 是 string,y 只有与 x 相同长度字顺序相同,结果 true,否则 false
  13. x 是 boolean,y 与 x 同为 true 或 false,结果 true,否则 false
  14. x 是 symbol,y 与 x 指向相同 symbol,结果 true,否则 false
  15. 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

# 小结

相等、全等、同值、零值相等比较,只关注正负 0,NaN,是否发生隐式类型转换即可

另外就是不同的 API,使用的比较算法不同

比如:

  1. Object.is 使用同值比较
  2. [].includes 使用零值相等比较
  3. switch ... case 语句、[].indexOf 使用全等比较

相等比较视具体的业务场景决定是否使用,规则较多,大致如下几个方面:

  1. 类型相等,用全等
  2. object 转为原始类型,先调用 [Symbol.toPrimitive],没有调用 toString,目前只有Date 、 Symbol 部署了 [Symbol.toPrimitive] 方法
  3. 类型不同原始类型向 number 类型转换

扫一扫,微信中打开

微信二维码