(NaN! = NaN)和(NaN! = = NaN)有什么区别?

首先我想说的是,我知道 isNaN()Number.isNaN()是如何工作的。我正在读大卫 · 弗拉纳根写的 绝对指南,他给出了一个如何检查值是否为 NaN的例子:

x !== x

这将导致 true当且仅当 xNaN

但是现在我有一个问题: 为什么他使用严格的比较? 因为看起来

x != x

行为也一样。使用这两个版本是否安全,或者我在 JavaScript 中遗漏了一些值,这些值将返回 x !== xtruex != xfalse

14572 次浏览

对于 NaN,!=!==做同样的事情。

然而,许多程序员在 JavaScript 中避免使用 ==!=。例如,道格拉斯·克罗克福特认为它们属于 JavaScript 语言的“ 糟糕的部分”,因为它们的行为出乎意料且令人困惑:

JavaScript 有两组相等运算符: ===!==,以及它们的邪恶孪生子 ==!=。好的那些按照你期望的方式工作。

... 我的建议是永远不要使用邪恶的双胞胎。相反,总是使用 ===!==

首先,让我指出 NaN是一个非常特殊的值: 根据定义,它不等于它自己。这来自于 JavaScript 编码所依据的 IEEE-754标准。“非数字”值永远不等于它本身,即使位是完全匹配的。(在 IEEE-754中并不一定是这样,它允许多个不同的“非数字”值。)这就是为什么会出现这种情况; JavaScript 中的所有其他值都等于它们自己,NaN只是特殊的。

... 我是否遗漏了 JavaScript 中的一些值,这些值将返回真值!= = x 和 x 的假值!= x?

不,你不是。!==!=之间的唯一区别是,如果需要使操作数的类型相同,后者将进行类型强制。在 x != x中,操作数的类型是相同的,因此它与 x !== x完全相同。

抽象相等操作的定义一开始就清楚地表明了这一点:

  1. 突然返回(x)。
  2. 突然返回(y)。
  3. 如果 Type (x)与 Type (y)相同,则

    返回执行严格相等比较 x = = = y 的结果。

  4. ...

前两个步骤是基本的管道。因此,实际上,==的第一步是查看类型是否相同,如果相同,则执行 ===!=!==只是否定的版本。

因此,如果弗拉纳根是正确的,只有 NaN将给出真的 x !== x,我们可以肯定,这也是真的,只有 NaN将给真的 x != x

许多 JavaScript 程序员默认使用 ===!==,以避免松散运算符类型强制带来的一些陷阱,但是在这种情况下,没有什么可以解读 Flanagan 使用严格与松散运算符的原因。

只是为了好玩,让我向您展示一个人工示例,其中 x不是 NaN,但操作员的行为不同。第一个定义:

Object.defineProperty(
self,
'x',
{ get: function() { return self.y = self.y ? 0 : '0'; } }
);

那我们就有了

x != x // false

但是

x !== x // true

我只是想指出,不使用全局对象生成 x !== x的不只是 NaN。有很多聪明的方法可以触发这种行为。下面是一个使用 getter 的例子:

var i = 0, obj = { get x() { return i++; }};
with(obj) // force dynamic context, this is evil.
console.log(x === x); // false

正如其他答案指出的那样,==执行类型强制,但与其他语言一样,标准 NaN 表示计算失败,而且出于充分的理由,它不等于 ==本身。

出于某种原因,人们认为这是 JS 的一个问题,但是大多数有双精度语言(即 C、 Java、 C + + 、 C # 、 Python 和其他语言)表现出这种确切的行为,人们对此并不介意。

有时候,图片比文字更好,检查这个 桌子(这就是为什么我把这个作为一个答案,而不是一个评论,因为它获得更好的可见性)。

您可以看到,严格相等比较(= = =)只有在类型和内容匹配时才返回 true,因此

var f = "-1" === -1; //false

而抽象相等比较(= =)只通过转换类型检查内容 * ,然后严格比较它们:

var t = "-1" == -1; //true

虽然在没有参考 ECMA的情况下,还不清楚 JavaScript 在进行比较时考虑了什么,但下面的代码在某种程度上可以得出结论。

 var howAmISupposedToKnowThat = [] == false; //true