7

我知道规则:

如果两个操作数的类型不同,JavaScript 会转换操作数,然后应用严格比较。如果任一操作数是数字或布尔值,则尽可能将操作数转换为数字;否则,如果任一操作数是字符串,则尽可能将另一个操作数转换为字符串。

所以,if("true")通过但if("true" == true)失败,因为它是句柄 if(NaN == 1)

我想知道当一个值是布尔值时,这背后的理性是什么。在其他弱类型语言(如 php)中,这是以不同方式处理的——如果一个值是布尔值,则另一个值被转换为布尔值以进行比较(而不是像 javascript 中那样将两者都转换为数字)。

我假设这个选择是==经过仔细考虑后为运营商做出的。任何人都可以解释为什么这是选择的功能吗?是否有一个常见的用例被选择来解决?我敢打赌这不仅仅是一个错误。

4

2 回答 2

8

来自 es-discuss@mozilla.org 邮件列表的 Brendan Eich 的快速回复:

考虑 Perl:

$ perl -e 'print 0 == "true";'
1

好的,理由很糟糕——但我在 1995 年 5 月在 AWK、Perl 4、Python 1.2 (IIRC)、TCL 的阴影下创建了 JS。

考虑到我应该比 Perl 更关注 AWK

$ awk 'END {print(0 == "0")}'
1D
$ awk 'END {print(0 == "")}'
0D

在某些方面,JS 的运算符通过转换为 NaN来==拆分 Perl(非数字字符串,例如"true"转换为0)和 AWK(仅"0"转换为)之间的差异。0这样,至少,我们有

js> 0 == ""
true
js> 0 == "true"
false

但完整的事实并不是我在仔细模仿其他语言。相反,一些致力于在类似 PHP 的服务器 (LiveWire) 中嵌入 JS(然后是“Mocha”)的 Netscaper 希望进行草率的转换,因此程序员可以匹配 HTTP 标头字符串(服务器端)或 HTML 表单字段(客户端),例如, 404等,没有程序员的明确强制。

但那是 90 年代,我急得要命,这些前 Borland Netscapers 坚持不懈。所以,正如我去年在 Strange Loop所说的,“我是个白痴!我给了他们想要的东西!”

隐式转换是我对 JS 仓促设计的最大遗憾,没有之一。甚至包括“与”!

有谁知道选择不将任何值与 == 运算符中的布尔值相比转换为布尔值的确切原因?

一般的想法是较窄的类型应该加宽。因此,true == 1接下来将 boolean 投影{false, true}到 上{0, 1},就像在 C++ 中一样。

但是为什么不扩大true到字符串,因为您示例中的另一个操作数是"true"?好问题。如果操作数是数字或布尔值,则将字符串作为数字进行比较的偏见源于 HTTP 标头和数字字符串 HTML 表单字段用例。再次,不是很好的理由,但这就是JS“工作”的方式:-|。

您可以在 ECMA-262 Edition 5.1 规范中看到这一点,11.9.3 The Abstract Equality Comparison Algorithm,第 6 步和第 7 步(根据第 4 步和第 5 步阅读):

4.如果Type(x)是Number,Type(y)是String,返回比较结果x == ToNumber(y)。
5.如果Type(x)是String,Type(y)是Number,返回比较结果ToNumber(x) == y。
6. 如果 Type(x) 是 Boolean,返回比较结果 ToNumber(x) == y。
7. 如果 Type(y) 为 Boolean,则返回比较结果 x == ToNumber(y)。

这一切都在一个大的“else 子句中,其中Type(x)Type(y)forx == y不一样。

抱歉,这里没有智慧的珍珠(原文如此)。除了隐式转换,==不要!=将操作数直接加宽(没有中间转换)到可以容纳其他操作数而不会丢失数据的最窄宽度。这种将字符串缩小为数字的做法只是一个烂摊子。

如果我们修复了这个问题,我们仍然有:

0 == "0"
1 == "1"
true != "1"
false != "0"

但我们也会有你的例子想要的:

true == "true"
false != ""

根据我将数字优先于字符串转换的偏好称为拙劣,我们不会有true == "1"or false == "0",因为它会从字符串缩小到数字。确实,缩小不会丢失任何位,并且可以扩大0"0"1回到"1",但我的意思是说明从 == 的隐式转换规范中删除所有数字上的字符串偏差会做什么。

这样的更改会破坏网络上的大量代码吗?我敢打赌它会。

有些人认为,除了任何隐式转换之外,还有一个使用===and的原因!==(因为他们从不转换),并且完全避开==and !=。其他人不同意(尤其是在测试时x == null,一种单操作员方式test x === null || x === undefined)。

由于网络在很大程度上是兼容的,直到非常旧的形式消亡,我们被 and 困住了==!=所以我说学习草率的相等运算符做什么是值得的。这样做后,在我看来,他们可能会在获胜的地方使用它们:当您知道操作数是相同类型时,例如

typeof x == "function", ETC。
x == null

否则,使用===and !==

如果操作数是数字或布尔值,则将字符串作为数字进行比较的偏见源于 HTTP 标头和数字字符串 HTML 表单字段用例。再次,不是很好的理由,但这就是JS“工作”的方式:-|。

还有一点需要注意:如果任何非数字、非空的字符串到数字的隐式转换尝试抛出一个例外。

这是 JS 设计位中另一个依赖于路径的偏差:在 JS1 或任何 ECMA-262 标准中直到 ES3 都没有 try/catch。

缺少异常处理也意味着undefined缺少obj.foo属性 getobj没有此类属性。这仍然在咬人,可能与隐式转换咬人一样多或更多==。它也是 web JS 获胜的“对象检测”模式的基础,它完全击败了我见过的所有其他版本控制方案,尤其是基于显式编号和选择加入的先验方案。

要是我花时间为对象检测添加一个存在运算符就好了,这样就可以写了

function emulateRequestAnimationFrame(...) {...}
if (!window.requestAnimationFrame?)
  window.requestAnimationFrame = emulateRequestAnimationFrame;

IOW,如果我window.noSuchProperty投掷但window.noSuchProperty?评估为真或false(细节仍然待定,请参阅“快速失败的对象解构”线程复兴,Nil 想法)。

于 2013-01-06T23:10:24.560 回答
3

我认为有必要进行一些澄清。根据 ECMA 规范,if 语句的整个表达式(括号内的部分)被转换为布尔值。

所以想象它是这样的:

if (ToBoolean("true" == true)) {  //false

对比

if (ToBoolean("true")) { //true

我想为什么 ToBoolean 强制被添加到 if 表达式的原因是为了确保表达式总是安全和正确地计算。

ToBoolean 将单个值强制为布尔值。比较不会将每个值强制为布尔值,因为您会得到一些非常奇怪的结果,所以这没有意义。它检查是否相等,这是一种不同的操作。至于为什么一个值不转换为布尔值时另一个值我不确定,但请尝试 Mozilla ECMA 邮件列表:https ://mail.mozilla.org/listinfo/es-discuss

看:

http://www.ecma-international.org/ecma-262/5.1/#sec-9.2

http://www.ecma-international.org/ecma-262/5.1/#sec-12.5

http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.1

http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3

http://www.ecma-international.org/ecma-262/5.1/#sec-8.7.1

于 2013-01-06T17:20:23.303 回答