前言
在语言中,使用比较来判断操作数是比较常见的行为
对于 js
这门弱语言来说, 比较主要有三种形式,分别是 ==
(宽松比较),===
(严格相等)和 Object.js
,特别是 ==(宽松比较) 中的类型转化常常令人迷惑,今天一文彻底搞懂
宽松比较 ==
==
应该是最为让人熟知的是会 「🔗自动类型转化」 , 我们一般会在 字符串
和 数字
或者 布尔
这些 基本类型 之间相互转化。
js
'1' == 1 // true
'1' == true // true
1 == false // false
如果操作数一方有 对象 呢?他们的转化规则是什么呢?
js
1 == [1] // ?
{a:1} == "{a:1}" // ?
在 🔗MDN
关于宽松比较中操作数中一方是对象 有这样的解释:
如果其中一个操作数是对象,另一个是基本类型,按此顺序使用对象的
@@toPrimitive()
(以"default"
作为提示),valueOf()
和toString()
方法将对象转换为基本类型。(这个基本类型转换与相加中使用的转换相同。)
其中 🔗@@toPrimitive 是 js
内部方法,主要作用是用于 「返回对象原始类型的方法」
js
const object1 = {
[Symbol.toPrimitive](hint) {
if (hint === 'number') {
return 42;
}
return null;
},
};
console.log(+object1); // 42
📌没有 @@toPrimitive
属性的对象通过以不同的顺序调用 valueOf()
和 toString()
方法将其转换为原始值
toPrimitive
一般不会自己定义,但是 valueOf
和 toString
方法一定是存在的,因为在object
上是有默认方法的。
一般情况下,我们也是不会自定义 valueOf
和 toString
方法的
那么常见对象默认的 valueOf
和 toString
的返回值是什么呢?

❤️对于对象来说,执行toString
可以返回字符串 [object Object]
❤️对于数组来说,toString
返回的是表示 🔗数组元素组成的字符串,相当于调用arr.join
方法
所以,我们可以得到如下结果:
当然我们可以自定义 valueOf
和 toString
方法,如果同时定义这两个方法,会 优先 调用 valueOf
方法,如果 valueOf
返回的不是一个基本类型,则会调用 toString
方法,如果 toString
返回的仍然不是 基本类型
,会导致 🔗TypeError
js
let myObj = {
// valueof 返回数字1
valueOf(){
return 1
},
// toString 返回字符串2
toString(){
return '2'
}
}
myObj == "2" // false
// 执行了 valueOf
myObj == 1 // true

js
let myObj = {
// valueof 返回引用类型
valueOf(){
return []
},
// toString 返回字符串2
toString(){
return '2'
}
}
// 执行了 toString
myObj == "2" // true
myObj == 1 // false

根据 🔗MDN关于宽松比较 的定义,我进行了一个简单的总结
- 特殊情况
- null / undefined 他们虽然是不同类型,但是是相等的
- NaN 与任何值相比都是 false,甚至与自己相比 都是 false
介绍完特殊情况,介绍一般情况
- 如果两个操作数 类型相同,执行严格比较
- 如果不相同,会执行 类型转化后 再次执行宽松比较,直到类型相同
- 如果其中有一个操作数是数值 , 另一个操作数是字符串 ,则将字符串 使用
Number方法
转化为相对应的数值,如果对字符串执行Number
失败,会返回NaN
- 如果一个操作数是 布尔值 ,则将布尔值 转换为数值
- 如果一个操作数是 对象,另一个操作数是 数值或者字符串,则将对象转化为原始值,然后执行步骤1或者步骤2
- 如果其中有一个操作数是数值 , 另一个操作数是字符串 ,则将字符串 使用
例子
- 一个操作数是数字,另一个是字符串
js
'123' == 123
会对 字符串 123
执行 Number
构造函数,得到 数字123
,然后执行步骤1, 数字123 === 数字123
,所以 '123' == 123
的结果为 true
- 一个操作数是布尔值
js
false == 0
false == '0'
当其中一个操作数是 布尔值 时,true
转换为 1,false
转换为 0
所以 false == 0
相当于 数字0 == 数字0
,所以返回 true
当另一个操作数是字符串的时候,布尔值依然会转化
会转化为 数字0 == 字符'0'
,此时的情况又回到了例子1, 所以也是返回 true
- 一个操作数是对象
js
let x = {
a:1
}
console.log(x == "[object Object]") // true
let x2 = [1,2,3]
// 相当于调用 join 方法
console.log(x2 == "1,2,3")
严格相等 === 与 Object.is
与 宽松相等
相比,🔗严格相等 要容易的多,因为 严格相等不进行类型转化,所以只需要判断两边操作数是否相等即可
Object.is 也不执行类型转化,那么它 与 ===
的区别在哪呢?
主要区别在 +-0
和 null
的判断上


Object.is
认为 +0
与 -0
是不相同的,NaN
与 NaN
是相同的,严格相等与之相反
总结
-
在比较两个操作数时,双等号(
==
)将执行类型转换,并且会按照 IEEE 754 标准对NaN
、-0
和+0
进行特殊处理(故NaN != NaN
,且-0 == +0
); -
三等号(
===
)做的比较与双等号相同(包括对NaN
、-0
和+0
的特殊处理)但不进行类型转换;如果类型不同,则返回false
; -
Object.is()
既不进行类型转换,也不对NaN
、-0
和+0
进行特殊处理(这使它和===
在除了那些特殊数字值之外的情况具有相同的表现) -
上述三个操作分别与 JavaScript 四个相等算法中的三个相对应:
-
🔗IsStrictlyEqual:
===
-
🔗SameValue:
Object.is()
| x | y | == | === | Object.is | | :---------------: | :---------------: | :-----: | :-----: | :-------: | | | undefined | undefined | ✅ true | ✅ true | ✅ true | | null | null | ✅ true | ✅ true | ✅ true | | true | true | ✅ true | ✅ true | ✅ true | | 'foo' | 'foo' | ✅true | ✅ true | ✅ true | | 0 | 0 | ✅ true | ✅ true | ✅ true | | +0 | -0 | ✅ true | ✅ true | ❌ false | | +0 | 0 | ✅ true | ✅ true | ✅ true | | -0 | 0 | ✅ true | ✅ true | ❌ false | | 0n | -0n | ✅ true | ✅ true | ✅ true | | 0 | false | ✅ true | ❌ false | ❌ false | | "" | false | ✅ true | ❌ false | ❌ false | | "" | 0 | ✅ true | ❌ false | ❌ false | | '0' | 0 | ✅ true | ❌ false | ❌ false | | '17' | 17 | ✅ true | ❌ false | ❌ false | | [1, 2] | '1,2' | ✅ true | ❌ false | ❌ false | | new String('foo') | 'foo' | ✅ true | ❌ false | ❌ false | | null | undefined | ✅ true | ❌ false | ❌ false | | null | false | ❌ false | ❌ false | ❌ false | | undefined | false | ❌ false | ❌ false | ❌ false | | { foo: 'bar' } | { foo: 'bar' } | ❌ false | ❌ false | ❌ false | | new String('foo') | new String('foo') | ❌ false | ❌ false | ❌ false | | 0 | null | ❌false | ❌ false | ❌ false | | 0 | NaN | ❌false | ❌ false | ❌ false | | 'foo' | NaN | ❌ false | ❌false | ❌ false | | NaN | NaN | ❌ false | ❌ false | ✅ true |
详细信息可以查看🔗相等性方法比较
在项目中,我个人推荐还是使用严格比较或者是Object.is
,虽然可能要多写一些转化代码,但是对于变量类型的认知更加清晰,也可以避免一些因类型而引发出的错误