Hello,大家好,我是 Sunday。
我们知道在 JS 中有两种判断相等的方式,即:== 和 ===。并且很多同学会认为“==”运算符(通常称为“松散相等”或简称“相等”)是不精准的。
“==”不关心数据类型,只关心值
例如如下代码:
1 == '1' // true
而“===”运算符,即“严格相等”,可以有效地验证操作数的值和类型:
1 === '1' // false
但是这种理解真的是完全正确的吗?最近有一个同学在面试的时候被深入问到了这个问题,咱们来看一下:
“==”和“===” 的执行逻辑
先说结论,其实:“==”和“===”这两个运算符都优先考虑数据类型,并在执行各自的算法之前执行类型检查。事实上,即使是“==”运算符在评估值之前也会验证类型。
根据 JavaScript 规范参考 ECMAScript 262 文档,概述了 IsLooselyEqual算法 ,负责处理“==”操作。这里说明了涉及 14 个步骤的综合过程。值得注意的是,这个过程大量涉及类型检查,如涉及Type(x)的初始步骤所证明的那样,并引入强制作为整个操作的重要方式。
图片
下方描述为翻译后的结果
// 在线地址:https://262.ecma-international.org/14.0/?_gl=1*1ddd25w*_ga*MTAzMTk1MjUwNS4xNzA0MTgwNDk0*_ga_TDCK4DWEPP*MTcwNDE4MDQ5NC4xLjAuMTcwNDE4MDQ5NC4wLjAuMA..&_ga=2.72864531.1838071013.1704180495-1031952505.1704180494#sec-islooselyequal
7.2.14 松散相等 ( x, y )
抽象操作 IsLooselyEqual 接受参数 x(ECMAScript 语言值)和 y(ECMAScript 语言值),并返回包含布尔值的正常完成或抛出完成。 它提供 == 运算符的语义。 调用时它执行以下步骤:
1. 如果 Type(x) 是 Type(y),则
A。 A。 返回 IsStrictlyEqual(x, y)。
2. 如果 x 为 null 并且 y 未定义,则返回 true。
3. 如果 x 未定义且 y 为 null,则返回 true。
4. 注意:此步骤已在 B.3.6.2 节中替换。
5. 如果 x 是数字且 y 是字符串,则返回! IsLooselyEqual(x, !ToNumber(y))。
6. 如果 x 是字符串且 y 是数字,则返回! IsLooselyEqual(!ToNumber(x), y)。
7. 如果 x 是 BigInt 并且 y 是 String,则
A。 令 n 为 StringToBigInt(y)。
b. 如果 n 未定义,则返回 false。
C。 返回 ! IsLooselyEqual(x, n)。
8. 如果 x 是 String 并且 y 是 BigInt,则返回! IsLooselyEqual(y, x)。
9. 如果 x 是布尔值,则返回! IsLooselyEqual(!ToNumber(x), y)。
10. 如果 y 是布尔值,则返回! IsLooselyEqual(x, !ToNumber(y))。
11. 如果 x 是字符串、数字、BigInt 或符号并且 y 是对象,则返回! IsLooselyEqual(x, ? ToPrimitive(y))。
12. 如果 x 是对象并且 y 是字符串、数字、BigInt 或符号,则返回! IsLooselyEqual(? ToPrimitive(x), y)。
13. 如果 x 是 BigInt 并且 y 是 Number,或者如果 x 是 Number 并且 y 是 BigInt,则
A。 A。 如果 x 不是有限的或 y 不是有限的,则返回 false。
b. b. 如果 ℝ(x) = ℝ(y),则返回 true; 否则返回 false。
14. 返回 false。
强制转换涉及将一种类型的值转换为另一种类型,可以通过有意操作显式转换,也可以通过 JavaScript 机制隐式转换,无需任何用户干预。
另外一个有趣的方法,当两种类型匹配时,将使用 IsStrictlyEqual 算法(步骤 1.a),该算法与“===”运算符使用的算法完全相同。
图片
翻译之后为:
// 在线地址:https://262.ecma-international.org/14.0/?_gl=1*1ddd25w*_ga*MTAzMTk1MjUwNS4xNzA0MTgwNDk0*_ga_TDCK4DWEPP*MTcwNDE4MDQ5NC4xLjAuMTcwNDE4MDQ5NC4wLjAuMA..&_ga=2.72864531.1838071013.1704180495-1031952505.1704180494#sec-isstrictlyequal
7.2.15 IsStrictlyEqual(x,y)
抽象操作 IsStrictlyEqual 接受参数 x(ECMAScript 语言值)和 y(ECMAScript 语言值)并返回布尔值。 它提供 === 运算符的语义。 调用时它执行以下步骤:
1. 如果 Type(x) 不是 Type(y),则返回 false。
2. 如果 x 是一个数字,那么
A。 返回 Number::equal(x, y)。
3. 返回 SameValueNonNumber(x, y)。
让我们深入研究 IsLooselyEqual的第五步(如果 x 是数字且 y 是字符串,则返回! IsLooselyEqual(x, !ToNumber(y))):
图片
根据提供的代码片段
1 == "1" // true
// x 是数字 (1)
// y 是字符串 ("1")
此处,调用 ToNumber (y)函数时会发生隐式 强制转换。此强制过程将值(y,即“1”)从其原始类型(在本例中为String)转换为不同的类型(在本例中为Number)。在比较值之前,此步骤涉及类型检查和强制,将两个值对齐到同一类型(Number),从而实现两个数字之间的直接比较。
1 == "1" // true
//步骤 1。
// -> 检查两种类型
// -> 两种类型不同
// -> 执行 IsLooselyEqual 的后续步骤
//步骤 2。
// -> "1 " (String) 将变成 1 (Number) - 强制机制
//Step 3.
// -> 将值 1(数字,从左侧)与值 1(数字,在右侧)进行比较
在这种情况下,严格相等运算符将返回 false,如前面提供的内容所示。由于两种类型不同,因此 === 会判定为 false:
1 === "1" // false
//步骤 1.
// -> 检查两种类型
// -> 两种类型不同
// -> 结束进程,返回 false
总结
两个比较运算符都会检查类型,但松散的相等运算符(==) 需要执行额外的步骤。
- 它首先检查比较值的类型,如果它们不同,则将它们对齐到相同类型(使用隐式强制),
- 然后继续进行值比较。 使用严格相等运算符(===)时,不涉及该额外步骤。在值不同的情况下,它直接返回 false。