道格拉斯·克罗克福德(Douglas Crockford)在JavaScript中的好部分中写道:


JavaScript有两组相等的运算符:===!==,以及它们的邪恶双胞胎==!=。优秀的产品以您期望的方式工作。如果两个操作数具有相同的类型并且具有相同的值,则===产生true,而!==产生false。当操作数是相同类型时,邪恶双胞胎会做正确的事情,但是如果操作数是不同类型,则它们会试图强制值。他们所遵循的规则是复杂而难忘的。这些是一些有趣的情况:

'' == '0'           // false
0 == ''             // true
0 == '0'            // true

false == 'false'    // false
false == '0'        // true

false == undefined  // false
false == null       // false
null == undefined   // true

' \t\r\n ' == 0     // true


传递性的缺乏令人担忧。我的建议是不要使用邪恶的双胞胎。相反,请始终使用===!==。刚刚显示的所有比较都使用false运算符产生===


鉴于这种明确的观察,是否曾经有一段时间使用==实际上是合适的?

评论

在很多地方都有意义。很明显,只要您要比较两个相同类型的事物(根据我的经验,这种情况经常发生),== vs ===就取决于偏好。其他时候,您实际上需要抽象比较(例如您在答案中提到的情况)。是否合适取决于任何给定项目的约定。

关于“很多地方”,以我的经验,无关紧要的案例要多于无关紧要的案例。您的经验可能有所不同;也许我们有不同类型项目的经验。当我查看默认情况下使用==的项目时,===脱颖而出,让我知道正在发生的重要事情。

我认为JavaScript在类型强制方面不够完善。就像BS语言一样,它应该具有更多的类型强制选项。

我使用==的地方是比较下拉列表ID(始终为char)和模型ID(通常为int)的情况。

当您不想处理为15个平台中的每个平台生产本机应用程序,而不必在拥有一个平台的每个平台的垄断性App Store上进行认证时,@ DevSolar Web开发就很有意义。

#1 楼

我将对==进行论证

道格拉斯·克罗克福德(Douglas Crockford),您所引用的道理以他的许多且通常非常有用的观点而闻名。虽然我在这种特殊情况下与克罗克福德在一起,但值得一提的是,这并不是唯一的意见。还有其他人,例如语言创建者Brendan Eich,没有看到==的大问题。该参数类似于以下内容:

JavaScript是一种行为*类型的语言。事物是根据它们可以做什么而不是其实际类型来对待的。这就是为什么您可以在NodeList或jQuery选择集上调用数组的.map方法的原因。这也是为什么您可以执行3 - "5"并取回有意义的东西的原因-因为“ 5”可以像数字一样起作用。

执行==相等时,您在比较变量的内容而不是类型。在某些情况下这很有用:




从用户那里读取数字-读取DOM中输入元素的.value?没问题!您不必开始强制转换它或担心它的类型-您可以立即将其==转换为数字并获取有意义的返回值。

需要检查已声明变量的“存在性”吗? -您可以== null,因为从行为上说null表示不存在任何内容,并且undefined也不存在。

需要检查您是否从用户那里得到了有意义的输入? -使用==参数检查输入是否为假,它将处理用户未输入任何内容或仅为您输入空白的情况,这可能正是您所需要的。

让我们看一下Crockford的示例并进行说明他们的行为:

'' == '0'           // got input from user vs. didn't get input - so false
0 == ''             // number representing empty and string representing empty - so true
0 == '0'            // these both behave as the number 0 when added to numbers - so true    
false == 'false'    // false vs got input from user which is truthy - so false
false == '0'        // both can substitute for 0 as numbers - so again true

false == undefined  // having nothing is not the same as having a false value - so false
false == null       // having empty is not the same as having a false value - so false
null == undefined   // both don't represent a value - so true

' \t\r\n ' == 0     // didn't get meaningful input from user vs falsey number - true 


基本上,==旨在根据JavaScript中基本体的行为方式工作,而不是基于它们的本质。尽管我个人不同意这种观点,但这样做绝对是有好处的-特别是如果您采用这种基于整个语言行为的类型来对待类型的范例。

*有些人可能更喜欢这个名称结构化类型比较常见,但有区别-在这里讨论差异并不十分感兴趣。

评论


这是一个很好的答案,我使用了所有三个“ for ==”用例。 #1&#3特别有用。

–克里斯·西里菲斯(Chris Cirefice)
15年1月6日在7:06

==的问题不是所有比较都没有用,而是规则难以记住,因此几乎可以保证会犯错误。例如:“需要检查您是否从用户那里得到有意义的输入吗?”,但是'0'是有意义的输入,而'0'== false为true。如果您使用过===,则必须明确地考虑一下,并且不会犯错。

– Timmmm
15年1月6日,12:03

“规则难以记住” <==这是使我无法执行Javascript中任何“有意义”的事情。 (以及导致基本calc练习出现问题的浮点数学)

–WernerCD
2015年1月6日14:37

3-“ 5”示例本身就提出了一个很好的观点:即使您专门使用===进行比较,这也只是变量在Javascript中的工作方式。没有办法完全摆脱它。

–贾里特·米拉德(Jarett Millard)
2015年1月6日14:56



@Timmmm注意,我在这里扮演恶魔的拥护者。我不在自己的代码中使用==,我发现它是一种反模式,我完全同意抽象等式算法很难记住。我在这里所做的是使人们为此辩护。

–本杰明·格林巴姆
2015年1月6日14:59

#2 楼

事实证明,jQuery广泛使用了构造方法

if (someObj == null) {
  // do something
}


作为等效代码的简写:

if ((someObj === undefined) || (someObj === null))  {
  // do something
}


这是ECMAScript语言规范§11.9.3(抽象平等比较算法)的结果,该算法规定了除其他事项外,

1.  If Type(x) is the same as Type(y), then  
    a.  If Type(x) is Undefined, return true.  
    b.  If Type(x) is Null, return true.



/>
2.  If x is null and y is undefined, return true.
3.  If x is undefined and y is null, return true.


这种特殊技术很常见,因此JSHint具有专门为其设计的标志。

评论


不公平地回答您自己的问题,我想回答这个问题:) == null或undefined是唯一不使用===或!==的地方

–pllee
2015年1月5日在22:40

公平地说,jQuery几乎不是模型代码库。多次阅读jQuery源代码是我最不喜欢的代码库之一,其中包含许多嵌套的三元数,不清楚的位,嵌套以及本应避免在实际代码中使用的东西。不过请不要相信我的话-阅读github.com/jquery/jquery/tree/master/src,然后将其与jQuery克隆的Zepto进行比较:github.com/madrobby/zepto/tree/master/src

–本杰明·格林巴姆
15年1月6日在7:18

还要注意,Zepto似乎默认为==,仅在需要时才使用===:github.com/madrobby/zepto/blob/master/src/event.js

–嘿
15年1月6日在7:24

@嘿,坦白说,Zepto也不是模型代码库-以使用__proto__而臭名昭著,而__proto__却一手强迫将其放入语言规范中,以免破坏移动网站。

–本杰明·格林巴姆
15年1月6日在9:07

@BenjaminGruenbaum并不是对代码库质量的判断,只是指出不同的项目遵循不同的约定。

–嘿
15年1月6日,19:57

#3 楼

检查nullundefined的值是一回事,正如已经详细解释的那样。

还有另一件事,==发光:

您可以像这样定义来自>=的比较(人通常从>开始,但是我觉得这更优雅):



a > b <=> a >= b && !(b >= a)


a == b <=> a >= b && b >= a


a < ba <= b留给读者练习。

我们知道,在JavaScript "3" >= 3"3" <= 3中,您将获得3 == "3"。您可以指出,通过解析字符串来实现字符串和数字之间的比较是一个可怕的想法。但是考虑到这就是它的工作方式,==绝对是实现该关系运算符的正确方法。

因此,==的真正好处是它与所有其他关系一致。换句话说,如果您这样写:

function compare(a, b) {
  if (a > b) return 1;
  if (a < b) return -1;
  return 0;
}


您已经隐式使用了==

现在有一个非常相关的问题:以数字和字符串的比较方式实现它是一个错误的选择吗?孤立地看,这似乎是一件很愚蠢的事情。但是在JavaScript和DOM的其他部分的上下文中,考虑到以下事实则相对实用:


属性始终是字符串
键始终是字符串(用例是您使用Object拥有从整数到值的稀疏映射)
用户输入和表单控制值始终为字符串(即使源匹配input[type=number]

出于多种原因,在需要时使字符串表现得像数字一样有意义。并假设字符串比较和字符串连接具有不同的运算符(例如::用于简明表示法和一种比较方法(可以在其中使用各种有关区分大小写的参数以及不区分大小写的参数)),实际上这将避免混乱。但是实际上,此运算符重载可能是“ JavaScript”中“ Java”的来源;)

评论


公平地说,> =并不能真正传递。在JS中很可能既没有> b也没有
–本杰明·格林巴姆
2015年1月6日在16:51

@BenjaminGruenbaum:这就像说+并不是真正可交换的,因为NaN + 5 == NaN + 5不成立。关键是> =可以与==一致的数字ish值一起使用。毫不奇怪,“非数字”就其本质而言不是数字式的;)

–back2dos
2015年1月6日在22:13

因此==的不良行为与> =的不良行为是一致的?太好了,现在我希望有一个> == ...

– Eldritch难题
2015年1月7日,13:41

@EldritchConundrum:正如我试图解释的那样,> =的行为与其余语言/标准API相当一致。从总体上讲,JavaScript的作用远不只是古怪的部分之和。如果您想要> ==,还需要一个严格的+吗?实际上,许多这些决定使许多事情变得容易得多。因此,我不会急于将他们视为贫困者。

–back2dos
15年1月7日,13:55

@EldritchConundrum:再次:关系运算符用于比较数值,其中一个操作数实际上可能是字符串。对于> =有意义的操作数类型,==也是有意义的-仅此而已。没有人说您应该将[[]]与false进行比较。在像C这样的语言中,这种胡说八道的结果是未定义的行为。以相同的方式对待它:不要这样做。而且您会没事的。您也不需要记住任何魔术规则。然后实际上很简单。

–back2dos
2015年1月7日在18:03



#4 楼

作为一名专业的数学家,我在Javscript的相同性运算符==(也称为“抽象比较”,“松散相等性”)中看到了尝试在实体之间建立等价关系的尝试,包括自反,对称和可传递。不幸的是,这三个基本属性中的两个失败了:

==不是自反的:

A == A可能为假,例如

NaN == NaN // false



==不是传递性的:

A == BB == C一起并不表示A == C,例如

'1' == 1 // true
1 == '01' // true
'1' == '01' // false


仅对称财产得以幸存:

A == B暗示B == A,无论如何,这种侵犯可能是不可想象的,并会导致严重的叛乱;)

为什么等价关系很重要?

/>因为这是最重要,最普遍的关系类型,因此得到众多示例和应用程序的支持。最重要的应用是将实体分解为等价类,
本身就是理解关系的一种非常方便和直观的方式。不能等效就导致缺少等效类,这又导致缺乏直观性和不必要的复杂性,这是众所周知的。

为什么为这样的问题写==是一个如此可怕的想法一个非等价关系?

,因为它破坏了我们的熟悉和直觉,因为从字面上看,任何有趣的相似,相等,同等,同构,同一性等关系都是等价的。

类型转换

JavaScript不再依赖直观的等效项,而是引入了类型转换:


如果运算符不是同一类型,则等于运算符将转换它们,然后进行严格比较。


但是类型转换如何定义?通过一系列复杂的规则(具有众多例外)吗?

试图建立对等关系

布尔值。显然,truefalse不同,应该位于不同的类中。

数字。幸运的是,数字的相等性已经很好地定义了,其中两个不同的数字永远不在同一个等效类中。在数学上就是这样。在JavaScript中,数字的概念由于存在更奇特的-0Infinity-Infinity而有些变形。我们的数学直觉表明0-0应该在同一类中(实际上-0 === 0true),而每个无穷大都是一个单独的类。

数字和布尔值。给定数字类,我们应该在哪里放置布尔值? false变得与0类似,而true变得与1类似,但没有其他编号:

true == 1 // true
true == 2 // false


这里是否有将true1放在一起的逻辑?公认1是可区分的,但是-1也是如此。我个人没有任何理由将true转换为1

甚至变得更糟:

true + 2 // 3
true - 1 // 0


所以true确实被转换了放入所有数字中的1
是否合乎逻辑?直观吗?答案留给运动了;)

但是呢:

1 && true // true
2 && true // true


唯一的xx && true的布尔值truex = true。这证明12(以及0以外的任何其他数字)都转换为true!它表明我们的转换没有另一个重要的特性-双射。意味着两个不同的实体可以转换为相同的实体。就其本身而言,这不一定是一个大问题。当我们使用这种转换来描述我们要称呼它的“相同”或“松散相等”的关系时,就会出现一个大问题。但是有一点很明确-它不会成为等价关系,也不会通过等价类直观地描述。

但是我们可以做得更好吗?

至少在数学上-肯定是的!
仅在false0相同的情况下,就可以构造布尔值和数字之间的简单等价关系类。所以false == 0将是唯一不平凡的松散相等。

字符串呢?

我们可以在开头和结尾处修剪空格以转换为数字,也可以我们可以忽略前面的零:

'   000 ' == 0 // true
'   0010 ' == 10 // true


所以我们得到了一个简单的字符串规则-修剪前面的空白和零。我们得到一个数字或空字符串,在这种情况下,我们将转换为该数字或零。或我们没有数字,在这种情况下,我们不进行转换,因此也就没有新的关系。

这样,我们实际上就可以对整个布尔值,数字集获得完美的等价关系和弦!除了……JavaScript设计师显然有另一种意见:

' ' == '' // false


因此,两个都转换为0的字符串突然变得不相似!为什么或为什么?根据规则,在严格相等的情况下,字符串精确地相等!正如我们所见,这条规则不仅破坏了传递性,而且是多余的!创建另一个运算符==使其与另一个===完全相同的意义是什么?

结论

松散相等运算符==如果可以遵守一些基本的数学定律。但令人遗憾的是,它的实用性受到了损害。

评论


那NaN呢?同样,除非强制使用特定的数字格式来与字符串进行比较,否则必须导致字符串不直观或不传递。

–所罗门·乌科(Solomon Ucko)
19年5月31日在19:56



@SolomonUcko NaN是坏公民:-)。对于任何等效的比较,无论是否直观,都可以并应该保持传递性。

–德米特里·扎伊采夫(Dmitri Zaitsev)
19年6月1日,下午3:16

#5 楼

是的,我遇到了一个用例,即当您将键与数值进行比较时:

for (var key in obj) {
    var some_number = foo(key, obj[key]);  // or whatever -- this is just an example
    if (key == some_number) {
        blah();
    }
}


我认为这自然得多以key == some_number而非Number(key) === some_numberkey === String(some_number)进行比较。

#6 楼

今天,我遇到了一个非常有用的应用程序。如果您想将01等填充数字与普通整数进行比较,则==可以正常工作。例如:

'01' == 1 // true
'02' == 1 // false


省去了将0转换为整数的麻烦。

评论


我很确定这样做的“正确”方法是'04'-0 === 4,或者可能是parseInt('04',10)=== 4

– Ratbum
2015年1月8日在10:56



我不知道您可以这样做。

–琼·雪诺(Jon Snow)
15年1月8日在10:57

我得到很多。

–琼·雪诺(Jon Snow)
2015年1月8日在11:34

@ratbum或+ '01'=== 1

–埃里克·拉格伦(Eric Lagergren)
2015年1月8日在22:53

'011'== 011 //在非严格模式下为false,在严格模式下为SyntaxError。 :)

– Brian S
2015年1月9日在16:36

#7 楼

我知道这是一个较晚的答案,但是关于nullundefined似乎存在一些可能的混淆,恕我直言,这使==变得邪恶,更使得缺乏可传递性,这已经足够糟糕了。考虑:

p1.supervisor = 'Alice';
p2.supervisor = 'None';
p3.supervisor = null;
p4.supervisor = undefined;


这是什么意思? “

p1的主管是名字“ None”。

p2明确地没有主管。

p3可以或可能有主管。我们不知道,我们不在乎,我们不应该知道(隐私问题?),因为这与我们无关。

当您使用p4时,您正在将==null这是完全不正确的。这两个词意味着完全不同的东西!说我没有主管只是因为我拒绝告诉你谁是我的主管错了!

我知道有些程序员并不关心undefinednull之间的区别或选择以不同的方式使用这些术语。如果您的世界没有正确使用undefinednull,或者您希望对这些术语做出自己的解释,那就顺其自然。我不认为这是个好主意。

现在,我对undefinednull都毫无疑问是没有问题的!完全可以说

if (p.supervisor) { ... }


,然后undefinednull将导致处理主管的代码被跳过。没错,因为我们不认识或没有主管。都好。但是两种情况并不相等。这就是为什么undefined错误。同样,事情可能是虚假的,并且会以鸭子的形式使用,这对于动态语言非常有用。它是正确的JavaScript,Pythonic,Rubyish等。但同样,这些条件也不相同。

而且不要让我开始使用非传递性:=="0x16" == 10而不是10 == "10"。是的,JavaScript是弱类型。是的,它是强制性的。但是,强制性永远都不应应用于平等。

顺便说一句,克罗克福德确实有很强的见解。但是你知道吗?他在这里是正确的!

FWIW我知道有,而且我个人也遇到过"10" == "0x16"方便的情况!就像将字符串输入数字,例如与0进行比较一样。但是,这是hack。对于不准确的世界模型,您可以权衡取舍。

TL; DR:虚假是一个很棒的概念。它不应该扩展到平等。

评论


感谢您展示不同的情况:)但是,您缺少了p5 ... typeof(p5.supervisor)=== typeof(undefined)的一种情况,其中supervisor甚至不存在:D

–TheCatWhisperer
18年2月1日在20:26