Skip to content
On this page

平等的价值

想象一下,参加一个只有几种面具的狂欢节。一群穿着相同服装的人会在房间里开玩笑、跳舞、走动。这会让人困惑!你可能会和两个人交谈,却没有意识到你实际上是在和同一个人交谈两次。或者你可能认为你在和一个人说话,但实际上,你在和两个不同的人说话!

如果您在JavaScript中没有一个明确的平等的心智模型,那么每天都像狂欢节一样——而且不是以一种好的方式。你无法确定你处理的是相同的值,还是两个不同的值。因此,您经常会犯错误——比如更改一个您不打算更改的值。 幸运的是,我们已经完成了在JavaScript中建立平等概念的大部分工作。它以一种非常自然的方式符合我们的心智模式。

Kinds of Equality

在JavaScript中,有几种相等,如果你已经写过JS了,你应该对下面的几种相等不陌生

  • Strict Equality: a === b (triple equals).
  • Loose Equality: a == b (double equals).
  • Same Value Equality: Object.is(a, b).

Object.is(a,b)

在JavaScript中,Object.is(a,b)告诉我们a和b是否有相同的值

console.log(Object.is(2, 2)); // true
console.log(Object.is({}, {})); // false

这叫做相同的值相等。

尽管方法名是Object.is并不特定于对象。它可以比较任意两个值,不管它们是否是对象!

检查一下你的直觉🥳

let dwarves = 7;
let continents = '7';
let worldWonders = 3 + 4;

提示一下:这段代码的心智模型是如下的

image-20220327131519517

现在,尝试告诉我一下下面代码的答案

console.log(Object.is(dwarves, continents)); // ?
console.log(Object.is(continents, worldWonders)); // ?
console.log(Object.is(worldWonders, dwarves)); // ?

下面是答案👇:

  1. Object.is(dwarves, continents)false 因为 dwarvescontinents 指向不同的值.
  2. Object.is(continents, worldWonders)false 因为 continentsworldWonders 指向不同的值.
  3. Object.is(worldWonders, dwarves)true 因为 worldWondersdwarves 指向相同的值.

但是对象呢? 到这里,您可能要担心对象了。您可能听说过等式对对象无效,或者它比较“引用”。如果你有这样的直觉,那就暂时把它们放在一边。 相反,请看下面的代码片段

let banana = {};
let cherry = banana;
let chocolate = cherry;
cherry = {};

记住,{}总是表示“创建一个新的对象值”。另外,请记住=的意思是“将左侧的导线指向右侧的值”。

image-20220327132233221

现在再来解答一下下面的问题?👇

console.log(Object.is(banana, cherry)); // ?
console.log(Object.is(cherry, chocolate)); // ?
console.log(Object.is(chocolate, banana)); // ?
  1. Object.is(banana, cherry)false 因为 bananacherry 指向不同的值.
  2. Object.is(cherry, chocolate)false 因为 cherrychocolate 指向不同的值.
  3. Object.is(chocolate, banana)true 因为 chocolatebanana 指向相同的值.

正如您所看到的,我们不需要任何额外的概念来解释相同的值相等性如何适用于对象。它是通过我们的思维模式自然产生的。这就是我所知道的一切!

严格相等

您以前可能使用过严格相等操作符

console.log(2 === 2); // true
console.log({} === {}); // false

同值平等与严格平等

Object.is===有什么区别呢?

  • Object.is和我们心智模型中的值相等保持一致
  • 严格相同绝大部门情况下也和心智模型一致,但是也有一些特殊情况

这两种不寻常的情况都涉及我们过去讨论过的“特殊数字”:

  1. NaN === NaNfalse,尽管它们的值相同。
  2. -0 === 0并且0 === -0true,尽管它们是不同的值。

第一个特殊的例子: NaN

NaN 是一个特殊的Number ,当我们做无效的数学时就会出现 0 / 0:

let width = 0 / 0; // NaN

进一步的计算 NaN 也将会给你一个 NaN :

let height = width * 2; // NaN

您可能不会故意这样做,但如果您的数据或计算存在缺陷,就会发生这种情况.

记住 NaN === NaN 永远都是 false:

console.log(width === height); // false

然而, NaNNaN是同一个值:

console.log(Object.is(width, height)); // true
image-20220327133517952

这很令人困惑。 NaN === NaN 为假的原因很大程度上是历史原因,所以我建议接受它作为生活的事实。如果您尝试编写一些检查值是否为 NaN 的代码(例如,打印警告),您可能会遇到这种情况。

function resizeImage(size) {
  if (size === NaN) {
		// 这将永远不会被记录:检查永远是假的!
    console.log('Something is wrong.');
  }
  // ...
}

相反,这里有一些方法(它们都有效!)来检查size是否为NaN:

  • Number.isNaN(size)
  • Object.is(size, NaN)
  • size !== size

最后一个可能特别令人惊讶。给它一些时间。如果您没有看到它是如何检测NaN的,请尝试重新阅读这一节并再次思考它。

size !== size有效,因为NaN === NaN是假的,正如我们已经知道的。所以反过来(NaN !== NaN)一定是真的。 因为NaN是唯一不是严格等于自身的值,size !== size只能表示size是NaN。

一个简单的历史: https://stackoverflow.com/questions/1565164/what-is-the-rationale-for-all-comparisons-returning-false-for-ieee754-nan-values/1573715#1573715 。确保开发人员能够以这种方式检测 NaN 是使 NaN === NaN 返回 false 的最初原因之一!这是在JavaScript出现之前就决定的

第二个特殊的例子: -0

在普通数学中,没有“负0”这样的概念,但在浮点数学中却存在实际原因。这里有一个有趣的事实。

Both 0 === -0 and -0 === 0 are always true:

let width = 0; // 0
let height = -width; // -0
console.log(width === height); // true

然而, 0-0不是同一个值:

console.log(Object.is(width, height)); // false
image-20220327134050806

编码练习

现在您知道了Object.is和=== 是如何工作的了,我有一个小的编码练习给你。你不需要完成它,但它是一个有趣的难题

写一个函数strictEquals(a, b),返回与a === b相同的值。你的实现不能使用===或!==操作符。

下面是我的答案 🐥

function strictEquals(a, b) {
    if (Object.is(a, b)) {
        if (Object.is(a, NaN) || Object.is(b, NaN)) {
            return false
        } else {
            return true
        }
    } else {
        if ((Object.is(a, -0) && Object.is(b, 0)) ||
            Object.is(b, -0) && Object.is(a, 0)) {
            return true
        } else {
            return false
        }
    }

}   // sudongyuer🐟

听到这些特殊的数字和他们的行为可能会让人不知所措。不要过分强调这些特殊情况! 它们并不常见。既然你知道它们的存在,你就会在实践中认识到它们。在大多数情况下,我们可以相信Object.is (a, b)a === b

宽松的平等

松散等号(双等号)是JavaScript的梦魇。下面是几个让你起鸡皮疙瘩的例子:

console.log([[]] == ''); // true
console.log(true == [1]); // true
console.log(false == [0]); // true

松散相等(也称为“抽象相等”)的规则是晦涩难懂的。许多编码标准完全禁止在代码中使用==和!=。 尽管Just JavaScript对应该或不应该使用哪些特性没有强烈的意见,但我们不会详细讨论松散相等性。它在现代代码库中并不常见,它的规则在语言或我们的思维模式中也没有发挥更大的作用

松散相等规则被广泛认为是JavaScript早期的一个糟糕设计决策,但如果您仍然好奇,可以在这里查看它是如何工作的。不要觉得有压力要记住它——你需要记住其他话题!

值得了解的一个相对常见的用例是

if (x == null) {
  // ...
}

这段代码相当于这样写:

if (x === null || x === undefined) {
  // ...
}

然而,即使是==的使用也可能在一些团队中引起争议。在使用==之前,最好讨论一下在团队代码库中可以容忍多少==。

回顾

JavaScript有几种等号。它们包括相同价值平等、严格平等和松散平等。

  • 理解这种平等有助于防止错误!
  • 你经常需要知道什么时候你在处理相同的值,什么时候你在处理两个不同的值。
  • 当我们画一个值和变量的图时,相同的值不能出现两次。Object.is(a,b)当变量a和b指向图上相同的值时(a, b)为真。
  • 相同的值,编写起来有点麻烦,但它也是最容易解释的,这就是我们开始使用它的原因

在实践中,您将使用严格相等,或a === b,最常见的。除了两种罕见的特殊情况外,它等价于相同的值相等:

  • NaN === NaNfalse, 即使它们是相同的值.

  • 0 === -0-0 === 0true, 但它们是不同的值.

  • 你可以查看 xNaN 通过 Number.isNaN(x).

  • Loose equality (==) 使用一组神秘的规则,通常会被避免.

Released under the MIT License.