Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zh-cn: sync the translayion of "Equality comparisons and sameness" #24195

Merged
merged 4 commits into from
Oct 29, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
---
title: JavaScript 中的相等性判断
title: 相等比较和相同
Copy link
Member

@yin1999 yin1999 Oct 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

相等性比较和相同性?(不确定是否会更好一点)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我想到一个数学上的定义,自反性。我理解“性”代表的是抽象。而这里我认为是在指代两个具体的是否相等,是否相同。

slug: Web/JavaScript/Equality_comparisons_and_sameness
l10n:
sourceCommit: 8e1184924387f88e2ee63a3c786b007aaf573105
---

{{jsSidebar("Intermediate")}}
Expand All @@ -11,24 +13,24 @@ JavaScript 提供三种不同的值比较运算:
- [`==`](/zh-CN/docs/Web/JavaScript/Reference/Operators/Equality)——宽松相等(两个等号)
- [`Object.is()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is)

选择哪个运算取决于你需要什么样的比较。简单来说:
选择哪种运算取决于你需要什么样的比较。简单来说:

- 在比较两个操作数时,双等号(`==`)将执行类型转换,并且会按照 IEEE 754 标准对 `NaN`、`-0` 和 `+0` 进行特殊处理(故 `NaN != NaN`,且 `-0 == +0`);
- 三等号(`===`)做的比较与双等号相同(包括对 `NaN`、`-0` 和 `+0` 的特殊处理)但不进行类型转换;如果类型不同,则返回 `false`;
- `Object.is()` 既不进行类型转换,也不对 `NaN`、`-0` 和 `+0` 进行特殊处理(这使它和 `===` 在除了那些特殊数字值之外的情况具有相同的表现)。

上述三个操作分别与 JavaScript 四个相等算法中的三个相对应:

- [IsLooselyEqual](https://tc39.es/ecma262/#sec-islooselyequal):`==`
- [IsStrictlyEqual](https://tc39.es/ecma262/#sec-isstrictlyequal):`===`
- [SameValue](https://tc39.es/ecma262/#sec-samevalue):`Object.is()`
- [SameValueZero](https://tc39.es/ecma262/#sec-samevaluezero):被许多内置运算使用
- [IsLooselyEqual](https://tc39.es/ecma262/multipage/abstract-operations.html#sec-islooselyequal):`==`
- [IsStrictlyEqual](https://tc39.es/ecma262/multipage/abstract-operations.html#sec-isstrictlyequal):`===`
- [SameValue](https://tc39.es/ecma262/multipage/abstract-operations.html#sec-samevalue):`Object.is()`
- [SameValueZero](https://tc39.es/ecma262/multipage/abstract-operations.html#sec-samevaluezero):被许多内置运算使用

请注意,这些算法的区别都与它们对原始类型值的处理有关;它们都不会比较参数是否具有理论上相似的结构。对于任何具有相同的结构,但不是同一对象本身的非原始类型对象 `x` 和 `y` ,上述所有形式都将计算为 `false`。
请注意,这些算法的区别都与它们对原始值的处理有关;它们都不会比较参数是否具有概念上相似的结构。对于任何具有相同的结构、但不是同一对象的非原始类型对象 `x` 和 `y` ,上述所有形式都将计算为 `false`。

## 使用 === 进行严格相等比较

严格相等比较两个值是否相等。两个被比较的值在比较前都不进行隐式转换。如果两个被比较的值具有不同的类型,这两个值是不相等的。否则,如果两个被比较的值类型相同,值也相同,并且都不是 number 类型时,两个值相等。最后,如果两个值都是 number 类型,当两个都不是 `NaN`,并且数值相同,或是两个值分别为 `+0` 和 `-0` 时,两个值被认为是相等的。
严格相等比较两个值是否相等。两个被比较的值在比较前都不进行隐式转换。如果两个被比较的值具有不同的类型,则认为这两个值不相等。如果两个被比较的值类型相同,值也相同,并且都不是 number 类型时,则认为这两个值相等。最后,如果两个值都是 number 类型,当两个都不是 `NaN`,并且数值相同,或是两个值分别为 `+0` 和 `-0` 时,两个值被认为是相等的。

```js
const num = 0;
Expand All @@ -47,7 +49,7 @@ console.log(obj === null); // false
console.log(obj === undefined); // false
```

在日常中使用严格相等几乎总是正确的选择。对于除了数值之外的值,严格相等使用明确的语义进行比较:一个值只与自身严格相等。对于数值,严格相等使用略加修改的语义来处理两个特殊情况:第一个情况是,浮点数 0 是不分正负的。区分 `+0` 和 `-0` 在解决一些特定的数学问题时是必要的,但是大部分情况下我们并不用关心。严格相等认为这两个值是全等的。第二个情况是,浮点数包含了 `NaN` 值,用来表示某些定义不明确的数学问题的解,例如:正无穷加负无穷。严格相等认为 `NaN` 与其他任何值都不全等,包括它自己。(等式 `(x !== x)` 成立的唯一情况是 `x` 的值为 `NaN`)
使用严格相等几乎总是正确的选择。对于除了数字之外的值,严格相等有着明确的语义:值仅与它自身相等。对于数字,严格相等使用稍微不同的语义处理两个特殊情况:第一个情况是,浮点数 0 是不分正负的。区分 `+0` 和 `-0` 在解决一些特定的数学问题时区分正负是必要的,但是大部分情况下我们并不用关心。严格相等认为这两个值是全等的。第二个情况是,浮点数包含了 `NaN` 值,用来表示某些定义不明确的数学问题的解,例如:正无穷加负无穷。严格相等认为 `NaN` 与其他任何值都不全等,包括它自己。(等式 `(x !== x)` 成立的唯一情况是 `x` 的值为 `NaN`)

除了 `===` 之外,数组索引查找方法也使用严格相等,包括 [`Array.prototype.indexOf()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)、[`Array.prototype.lastIndexOf()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf)、[`TypedArray.prototype.index()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/indexOf)、[`TypedArray.prototype.lastIndexOf()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/lastIndexOf) 和 [`case`](/zh-CN/docs/Web/JavaScript/Reference/Statements/switch) 匹配。这意味着你不能使用 `indexOf(NaN)` 查找数组中 `NaN` 值的索引,也不能将 `NaN` 用作 `case` 值在 `switch` 语句中匹配任何内容。

Expand All @@ -61,7 +63,7 @@ switch (NaN) {

## 使用 == 进行宽松相等比较

宽松相等是*对称的*:对于任何 `A` 和 `B` 的值,`A == B` 总是与 `B == A` 具有相同的语义(除了转换应用的顺序)。使用 `==` 执行宽松相等的行为如下:
宽松相等是*对称的*:对于任何 `A` 和 `B` 的值,`A == B` 总是与 `B == A` 具有相同的语义(除了转换的应用顺序)。使用 `==` 执行宽松相等的行为如下:

1. 如果操作数具有相同的类型,则按如下方式进行比较:
- 对象(Object):仅当两个操作数引用同一个对象时返回 `true`。
Expand All @@ -76,11 +78,11 @@ switch (NaN) {
- 如果是相同的类型,使用步骤 1 进行比较。
- 如果其中一个操作数是符号而另一个不是,返回 `false`。
- 如果其中一个操作数是布尔值而另一个不是,则[将布尔值转换为数字](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number#number_强制转换):`true` 转换为 1,`false` 转换为 0。然后再次对两个操作数进行宽松比较。
- 数字与字符串:[将字符串转换为数字](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number#number_强制转换)。转换失败将导致 `NaN`,这将保证相等比较为 `false`。
- 数字与大整型:按数值进行比较。如果数字的值为 ±∞ 或 `NaN`,返回 `false`。
- 数字与字符串:[将字符串转换为数字](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number#number_强制转换)。转换失败的结果为 `NaN`,这将保证相等比较为 `false`。
- 数字与大整型:按数字的值进行比较。如果数字为 ±Infinity 或 `NaN`,返回 `false`。
- 字符串与大整型:使用与 [`BigInt()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/BigInt/BigInt) 构造函数相同的算法将字符串转换为大整型数。如果转换失败,返回 `false`。

一般而言,根据 ECMAScript 规范,所有原始类型和对象都不与 `undefined` 和 `null` 宽松相等。但是大部分浏览器允许非常有限的一类对象(即,所有页面中的 `document.all` 对象)在某些情况下表现出*模拟* `undefined` 值特性。宽松相等就是这些情况之一:当且仅当 A 是一个*模拟* `undefined` 的对象时,`null == A` 和 `undefined == A` 将会计算得到 `true`。在其他所有情况下,一个对象都不会与 `undefined` 或 `null` 宽松相等。
一直以来,根据 ECMAScript 规范,所有原始值和对象都不与 `undefined` 和 `null` 宽松相等。但是大部分浏览器允许非常有限的一类对象(即,所有页面中的 `document.all` 对象)在某些情况下表现出*模拟* `undefined` 值特性。宽松相等就是这些情况之一:当且仅当 A 是一个*模拟* `undefined` 的对象时,`null == A` 和 `undefined == A` 将会计算得到 `true`。在其他所有情况下,一个对象都不会与 `undefined` 或 `null` 宽松相等。

在大多数情况下,不建议使用宽松相等。使用严格相等进行比较的结果更容易预测,并且由于缺少类型强制转换可以更快地执行。

Expand Down Expand Up @@ -141,13 +143,13 @@ function sameValueZero(x, y) {
}
```

零值相等与严格相等的区别在于其将 `NaN` 视作是相等的,与同值相等的区别在于其将 `-0` 和 `0` 视作相等的。这使得它在搜索期间通常具有最实用的行为,特别是在与 `NaN` 一起使用时。它被用于 [`Array.prototype.includes()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/includes)、[`TypedArray.prototype.includes()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/includes)[`Map`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map) 和 [`Set`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set) 方法用来比较键的相等性
零值相等与严格相等的区别在于其将 `NaN` 视作是相等的,与同值相等的区别在于其将 `-0` 和 `0` 视作相等的。这使得它在搜索期间通常具有最实用的行为,特别是在处理 `NaN` 。它被用于 [`Array.prototype.includes()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/includes)、[`TypedArray.prototype.includes()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/includes),以及 [`Map`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map) 和 [`Set`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set) 的需要比较键的相等性的方法

## 相等性方法比较

在比较双等号和三等号时,人们通常说一个是另一个的“增强”版本。例如,双等号可以被称为三等号的扩展版本,因为前者可以执行后者的所有操作,但是会对其操作数进行类型转换——例如 `6 == "6"`。或者,也可以说双等号是基础,而三等号是增强版本,因为它要求两个操作数是相同的类型,因此增加了额外的约束。

然而,这种思维方式意味着相等比较形成了一个一维的“光谱”,其中“完全严格”位于一端,“完全宽松”位于另一端。这个模型在 {{jsxref("Object.is")}} 方面存在缺陷,因为它既不比双等号“宽松”,也不比三等号“严格”,也不处于两者之间(可以说既比双等号严格,又比三等号宽松)。从下面的相同比较表中,我们可以看出这是由于 {{jsxref("Object.is")}} 处理 {{jsxref("NaN")}} 的方式。请注意,如果 `Object.is(NaN, NaN)` 求值得到 `false`,我们*可以*说它适合宽松/严格光谱,作为三等号的更严格形式,可以区分 `-0` 和 `+0` 。然而,{{jsxref("NaN")}} 的处理意味着这是不正确的。不幸的是,{{jsxref("Object.is")}} 必须根据其特定特征来考虑,而不是根据其相等运算符的宽松度或严格度来考虑。
然而,这种思维方式意味着相等比较形成了一个一维的“光谱”,其中“完全严格”位于一端,“完全宽松”位于另一端。这个模型对 {{jsxref("Object.is")}} 而言存在缺陷,因为它既不比双等号“宽松”,也不比三等号“严格”,也不处于两者之间(可以说既比双等号严格,又比三等号宽松)。从下面的相同比较表中,我们可以看出这是由于 {{jsxref("Object.is")}} 处理 {{jsxref("NaN")}} 的方式。请注意,如果 `Object.is(NaN, NaN)` 求值得到 `false`,我们*可以*说它适合宽松/严格光谱,作为三等号的更严格形式,可以区分 `-0` 和 `+0` 。然而,{{jsxref("NaN")}} 的处理意味着这是不正确的。不幸的是,{{jsxref("Object.is")}} 必须根据其特定特征来考虑,而不是根据其相等运算符的宽松度或严格度来考虑。

| x | y | `==` | `===` | `Object.is` | `SameValueZero` |
| ------------------- | ------------------- | ---------- | ---------- | ----------- | --------------- |
Expand Down Expand Up @@ -186,7 +188,7 @@ function sameValueZero(x, y) {

- [`-`(一元减)](/zh-CN/docs/Web/JavaScript/Reference/Operators/Unary_negation)

- : 考虑以下例子
- : 注意下面的示例

```js
const stoppingForce = obj.mass * -obj.velocity;
Expand Down