【问题标题】:Javascript apparent madness [duplicate]Javascript明显的疯狂[重复]
【发布时间】:2012-05-11 17:29:34
【问题描述】:

可能重复:
Conflicting boolean values of an empty JavaScript array

这个事实背后的原因是什么

[ ([] == false), ([] ? 1 : 2) ]

返回[true, 1]?

换句话说,一个空列表在布尔上下文中逻辑上为真,但等于false

我知道使用=== 可以解决问题,但这个明显完全不合逻辑的选择背后的解释是什么?

换句话说,这是否被认为是语言中的错误,只是无意中发生的事情,无法修复,因为为时已晚,或者真的在语言设计中,有人认为这种明显的疯狂很酷我确定这让很多程序员感到很困惑?

关于这种情况如何发生的技术解释既令人惊讶又令人恐惧,但我对这种设计背后的内容更感兴趣。

编辑

我接受了非常详细的 Nick Retallack 解释,即使它只是关于为什么 []==false 为真的技术原因:令人惊讶的是,它发生的原因是 [] 转换为字符串是一个空字符串,而空字符串数值是特殊情况下为 0 而不是显然更合乎逻辑的NaN。以空对象为例,比较 ({}) == false 返回 false,因为空对象的字符串表示不是空字符串。

我的好奇心仍然存在,这一切只是出乎意料(不幸的是现在固化在一个标准中)。

【问题讨论】:

标签: javascript language-design


【解决方案1】:

这里的混淆在于 JavaScript 中“falsy”的定义,它(与流行的看法相反)与 == false 不同。

Falsy 实际上是指具有布尔等效值 false 的值,而不是结果 == false 的表达式。 JavaScript 中唯一的 Falsy 值是:false0""nullundefinedNaN。因此,这些值中的任何一个——或任何评估为这些值之一的表达式(如在if 语句中或使用三元运算符)——都是虚假的。

这是我在 JavaScript 中汇总的虚假/真实值的表格,它应该有助于解释整个问题。 http://jsfiddle.net/philipwalton/QjSYG/

【讨论】:

  • 很棒的桌子!然而值得注意的是,这里的混淆似乎是因为[] == false 的计算结果为假,因为它与[].toString() == false 相同,所以最终它是'' == false
  • @jmar777 你确定吗?如果我在控制台中运行[] === "",我会得到错误,那么[] 怎么可能与[].toString() 相同。你能引用一些东西来证明[] == false 等同于[].toString == false
  • @Philip 区别在于=====。当你只使用双等号时,类型强制是允许的。技术上发生的事情(==)是 JavaScript 引擎说“[] 等于 ''?”。答案显然是否定的,所以它说“好吧,让我尝试一些类型的强制......”。现在的问题是“'' 是否等于 ''?”,现在是正确的。但是,当您使用 === 运算符时,第二步永远不会发生,因此它的计算结果为 false。
  • 您可以在此处了解其工作原理:developer.mozilla.org/en/JavaScript/Reference/Operators/…。具体来说,“...如果其中一个操作数是字符串,则如果可能,另一个操作数将转换为字符串。”
  • @jmar777,我了解===== 之间的区别,我主要是在寻找JavaScript 解释器实际上在[] 上调用toString 的证据。看起来这里是对幕后发生的事情的解释:bclary.com/2004/11/07/#a-11.9.3
【解决方案2】:

让我们来了解一下技术。我将用ECMAScript Standard 262 中的引号来解释逻辑。

[] ? 1 : 2 的表达式很简单:

11.12 条件运算符 (? :)

  • 令 lref 为评估 LogicalORExpression 的结果。
  • 如果 ToBoolean(GetValue(lref)) 为真,则
    • 让 trueRef 成为评估第一个 AssignmentExpression 的结果。
    • 返回 GetValue(trueRef)。
  • 其他
    • 设 falseRef 为计算第二个 AssignmentExpression 的结果。
    • 返回 GetValue(falseRef)

9.2 ToBoolean

  • 未定义:假
  • 空:假
  • 布尔值:结果等于输入参数(无转换)。
  • 数字:如果参数为 +0、0 或 NaN,则结果为假;否则结果为真。
  • 字符串:如果 参数是空字符串(其长度为零);否则 结果是真的。
  • 对象:真

原来如此。


现在来看看使用双等号运算符会发生什么。也许这将有助于解释为什么你不应该这样做。

== 的行为在第 11.9.3 节:抽象等式比较算法中进行了解释。

对于 x == y,其中 x = [] 和 y = false,会发生这种情况:

11.9.3:抽象等式比较算法

如果 Type(y) 为 Boolean,则返回比较结果 x == ToNumber(y)

9.3 ToNumber

如果参数是,则结果是 +0 错误

现在我们有 [] == 0

11.9.3:抽象等式比较算法

如果 Type(x) 是 Object 并且 Type(y) 是 String 或 Number,则返回 比较结果 ToPrimitive(x) == y

9.1 ToPrimitive

返回对象的默认值。对象的默认值 通过调用 [[DefaultValue]] 的内部方法来检索 对象,传递可选提示 PreferredType。的行为 [[DefaultValue]] 内部方法由本规范定义 适用于 8.12.8 中的所有原生 ECMAScript 对象。

8.12.8 默认值:

当调用 O 的 [[DefaultValue]] 内部方法时没有 提示,那么它的行为就好像提示是 Number

  • 设 valueOf 为使用参数“valueOf”调用对象 O 的 [[Get]] 内部方法的结果。
  • 如果 IsCallable(valueOf) 为真,那么,
    • 令 val 为调用 valueOf 的 [[Call]] 内部方法的结果,其中 O 为 this 值和一个空参数列表。
    • 如果 val 是原始值,则返回 val
  • 令 toString 为使用参数“toString”调用对象 O 的 [[Get]] 内部方法的结果。
  • 如果 IsCallable(toString) 为真,则
    • 令 str 为调用 toString 的 [[Call]] 内部方法的结果,其中 O 为 this 值,参数列表为空。
    • 如果 str 是原始值,返回 str

我假设这首先尝试 valueOf 然后拒绝它,因为结果与您开始使用的数组相同。然后它在 Array 上调用 toString,这似乎被普遍实现为一个逗号分隔的值列表。对于像这样的空数组,这会导致空字符串。

现在我们有 '' == 0

11.9.3:抽象等式比较算法

如果 Type(x) 是 String 且 Type(y) 是 Number,则返回 比较 ToNumber(x) == y

9.3.1 ToNumber 应用于字符串类型

空或仅包含空格的 StringNumericLiteral 是 转换为 +0

现在我们有 0 == 0

11.9.3:抽象等式比较算法

如果 x 与 y 的数值相同,则返回 true

太棒了。这是真的。到达这里的方式相当复杂。

【讨论】:

  • 所以有人阅读了我评论中的链接:D。
  • 这是一个了不起的答案。
  • @NickRetallack 你经常编辑你的答案,一开始它很像我评论中的链接forums.whirlpool.net.au/archive/966449#r15310322。当然你的答案现在更详细了。
猜你喜欢
  • 1970-01-01
  • 2016-09-12
  • 1970-01-01
  • 2011-10-11
  • 1970-01-01
  • 2010-12-12
  • 1970-01-01
  • 1970-01-01
  • 2014-04-25
相关资源
最近更新 更多