JS中的==运算符
JS中的==运算符
JunsJS 中的==
运算符
😡 学 JS 的肯定被==
运算符折磨过,他会发生隐式类型转换,而且里面的逻辑也比较复杂,如果面试一问,感觉直接寄 😫。所以这里来看看他到底是个啥机制,至少要能说出来些啥,后面也可以来引出===
、Object.is()
还有SameValueZero (零值相等)算法
探讨 JS 中的相等机制。
宽松相等算法
JS 的行为遵循 ES 语言说明书,其中这个==
的底层原理定义在IsLooselyEqual(x, y),这个也叫宽松相等算法。
这里直接翻译一下他的定义:
Type(x) === Type(y)
,return x === y
。x === null && y === undefined
,true。x === undefined && y === null
,true。- (兼容一些非 ES 语言的对象)
- 若 x 是
Object
类型且 x 有一个[[IsHTMLDDA]]
内部插槽,且y === undefined || y === null
,true。 - 若
x === undefined || x === null
,且 y 是Object
类型,且 y 有一个[[IsHTMLDDA]]
内部插槽,true。
- 若 x 是
Type(x) === Number && Type(y) === String
,return IsLooselyEqual(x, ToNumber(y))。Type(x) === String && Type(y) === Number
,return IsLooselyEqual(ToNumber(x), y)。Type(x) === BigInt && Type(y) === String
let n = StringToBigInt(y)
n === undefined
,return false。return IsLooselyEqual(x, n)
Type(x) === String && Type(y) === BigInt
,return IsLooselyEqual(y, x)
Type(x) === Boolean
,return IsLooselyEqual(ToNumber(x), y)。- 同上,xy 互换。
- x 是
String || Number || BigInt || Symbol
,y 是Object
,return IsLooselyEqual(x, ToPrimitive(y))
- 同上,xy 互换。
- 若 x 是
BigInt
类型且 y 是Number
类型,或者 x 是Number
类型且 y 是BigInt
类型,则
a. 若 x 不是有穷数(not finite)或者 y 不是有穷数,则 return false。
b. 若ℝ(x) = ℝ(y)
,则 return true,否则 return false。 - false。
手写
下面直接手写实现来看看
const isUndefined = $ => typeof $ === 'undefined' |
规律
👀 了实现,现在可以总结出一个规律了
- null 只能和 undefined 宽松相等
- 原始类型优先,对象转原始值
- 数字类型优先,布尔值转数字
所以说 null == 0 = false
,他直接走到了最后一步的 false。
MDN 总结:
- 类型相同时
- Object: 引用值相同为 true
- String: 字符串相同为 true
- Number: 数字相同为 true,
+0 == -0
,如果有一个是NaN
,就返回 false - BigInt: 大整数相同为 true
- Boolean: 布尔值相同为 true
- Symbol: 引用相同时为 true,
Symbol() != Symbol()
- 有一个是 null / undefined,另一个也必须是 null / undefined,才是 true,否则 false
- 一个是基本类型,一个是对象,那么按这个顺序将对象转基本类型再比较:
@@toPrimitive() -> valueOf() -> toString()
有一方为对象
在 MDN 中如下解释:
如果其中一个操作数是对象,另一个是基本类型,按此顺序使用对象的 @@toPrimitive()(以 “default” 作为提示),valueOf() 和 toString() 方法将对象转换为基本类型。(这个基本类型转换与相加中使用的转换相同。)
toPrimitive 一般不会自己定义,但是 valueOf 和 toString 会,所以可以重写这两个方法来改变==
的行为。
先看看他们的默认返回值是什么:
const obj = { |
可以看出,对象的valueOf
默认返回对象本身,toString
默认返回[object Object]
,数组的valueOf
默认返回数组本身,toString
默认返回数组的内容,相当于 arr.join(‘,’)。
因此就可以直接写出这样的比较:
const obj = { |
我们可以知道,优先调用的是valueOf
,但是如果他返回的不是一个基本类型,那么就会调用toString
。
const obj = { |
这里就是因为valueOf
返回了一个基本类型,所以就不会调用toString
了。
const obj = { |
这里就是因为 valueOf 返回的不是基本类型,所以就会调用 toString 了。
所以我们也可以实现这样的比较:
String.prototype.valueOf = function () { |
常见误区
==
不比较类型
看代码就知道了,会比较类型,然后能判定是否要类型转换==
一定会隐式类型转换
不一定,如果类型相同,直接返回===
的结果- 只有
==
会触发隐式类型转换
并不是