(NaN != NaN) 和 (NaN !== NaN) 有啥区别?

Posted

技术标签:

【中文标题】(NaN != NaN) 和 (NaN !== NaN) 有啥区别?【英文标题】:What is the difference between (NaN != NaN) and (NaN !== NaN)?(NaN != NaN) 和 (NaN !== NaN) 有什么区别? 【发布时间】:2016-03-19 15:11:32 【问题描述】:

首先我想提一下,我知道isNaN()Number.isNaN() 是如何工作的。我正在阅读 David Flanagan 的 The Definite Guide,他举例说明了如何检查值是否为 NaN

x !== x

这将导致true 当且仅当xNaN

但现在我有一个问题:他为什么要使用严格比较?因为好像

x != x

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

【问题讨论】:

这可能是弗拉纳根更喜欢!== 支票而不是!= 支票。据我所知,x != x 没有其他价值。但是有两组不同的 JavaScript 开发人员:喜欢!= 的人和喜欢!== 的人,无论是速度、清晰度、表达力等。 当严格比较的行为方式相同时,为什么要使用松散比较? @Raulucco:NaN 不是唯一类型,它是一个数字。它是一个不等于自身的唯一 标题似乎误导了人。我建议将其更改为“x != x 与 x !== x 不同吗?” @femmestem:Giorgi 说“在这种情况下”,这是风格问题。他是正确的。当操作数的类型不同时它是不是样式,但是当它们相同时它是样式。另外:Flanagan 正在将=== 与 NaN 进行比较,以表明 NaN 不等于自身。他没有“错”,他将其作为一种教学练习,证明它不起作用。 【参考方案1】:

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

...我是否在 JavaScript 中遗漏了一些值,它将为 x !== x 返回 true,为 x != x 返回 false?

不,你不是。 !==!= 之间的唯一区别是后者将在必要时进行类型强制以使操作数的类型相同。在x != x中,操作数的类型是一样的,所以和x !== x完全一样。

这从Abstract Equality Operation的定义开始就很清楚了:

    ReturnIfAbrupt(x)。 ReturnIfAbrupt(y)。

    如果 Type(x) 与 Type(y) 相同,那么

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

    ...

前两个步骤是基本的管道。所以实际上,== 的第一步是查看类型是否相同,如果是,则改为使用===!=!== 只是它的否定版本。

因此,如果 Flanagan 是正确的,即只有 NaN 会为 x !== x 提供 true,那么我们可以确定只有 NaN 会为 x != x 提供 true 也是正确的。

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

【讨论】:

我重读了4.9.1 - Equality and Inequality Operators 部分,这似乎是答案。 ===比较的关键点是:If the two values have the same type, test them for strict equality as described above. If they are strictly equal, they are equal. If they are not strictly equal, they are not equal @GiorgiNakeuri:我不确定你指的是什么 4.9.1,也许是 Flanagan 的书?但这基本上是在说上面规范中的引述,是的。 我接受这个,因为它以正式和精确的方式回答了我的问题。谢谢你的解释! @Moshe:“实时绑定”是什么意思? (该术语没有出现在规范中。)你的意思是像GOTO 0's example 这样a 实际上是一个函数并且不会两次返回相同的值吗?这与!== 为真的 value 不同,这是 OP 所要求的。它只是一个返回不同值的函数。 foo() !== foo() 也不一定正确,因为 foo 可能在每次调用时返回不同的值。 @Moshe 嗯,这是一种搞乱属性和吸气剂的超级讨厌的方式。但它似乎与 GOTO 0 的示例几乎相同,只是多了一层间接。【参考方案2】:

有时,图像比文字更好,请查看table(这就是我将其作为答案而不是评论的原因,因为它可以获得更好的可见性)。

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

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

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

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

虽然不清楚,但在没有咨询 ECMA 的情况下,JavaScript 在比较时考虑了什么,下面的代码评估为 true。

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

【讨论】:

【参考方案3】:

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

但是,许多程序员在 JavaScript 中避免使用 ==!=。例如,Douglas Crockford 将它们视为 JavaScript 语言的“bad parts”之一,因为它们的行为方式出乎意料且令人困惑:

JavaScript 有两组相等运算符:===!==,以及它们的邪恶双胞胎 ==!=。好的工作按您的预期工作。

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

【讨论】:

问题不在于 NaN(尽管有标题)。问题是“我是否在 JavaScript 中遗漏了一些值,它将为 x !== x 返回 true,为 x != x 返回 false?” @T.J.Crowder 两个问题,真的。第一个问题是“使用两个版本是否安全”,答案是两个版本是等效的。我确实喜欢您的“幕后”答案,它详细解释了所有内容。【参考方案4】:

我只想指出NaN 并不是唯一不使用全局对象就可以产生x !== x 的东西。有很多聪明的方法可以触发这种行为。这是一个使用 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 等)都表现出这种确切的行为,人们对此很好。

【讨论】:

是的,这正是@T.J.Crowder 在对 GOTO_0 答案的评论中提到的,不是吗? 您能否阐明如何在这些其他语言中获得模棱两可的类型强制?【参考方案5】:

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

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

那么我们有

x != x // false

但是

x !== x // true

【讨论】:

哈! :-) 但这实际上是foo() != foo(),其中 foo 返回 1 然后 2。例如,不一样,它只是比较不同的值。

以上是关于(NaN != NaN) 和 (NaN !== NaN) 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

生成 n 个不是 ∞ 或 NaN 的不同浮点数(在 Go 中)

python数据分析-04Nan的类型处理

在 0 和 1 之间进行归一化,忽略 NaN

(NaN != NaN) 和 (NaN !== NaN) 有啥区别?

使用 Infinity 和 NaN 禁用异常

在 javascript 数组中查找 NaN 的索引