在JavaScript中使用==是否有意义?


276

Douglas Crockford 在JavaScript中的Good Parts中写道:

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===操作员进行的。

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


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

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

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

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

11
如果您不想处理为15个平台中的每个平台生成本机应用程序,而不必拥有每个平台的垄断性App Store上的认证,则@DevSolar Web开发很有意义。
Damian Yerrick

Answers:


232

我要为 ==

您引用的道格拉斯·克罗克福德(Douglas Crockford)以他的许多且常常是非常有用的观点而闻名。虽然我在这种特殊情况下与克罗克福德在一起,但值得一提的是,这并不是唯一的意见。像语言创造者布伦丹•艾希(Brendan Eich)这样的人并没有看到大问题==。该参数有点像以下内容:

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

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

  • 从用户那里读取数字 -读取.valueDOM中输入元素的?没问题!您不必开始转换它或担心其类型-您可以立即将==其转换为数字并获取有意义的内容。
  • 是否需要检查已声明变量的“存在”?-您可以== 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不是基于他们什么都是。尽管我个人不同意这种观点,但这样做绝对是有好处的-特别是如果您采用这种基于整个语言行为的类型来对待类型的范例。

*有些人可能更喜欢名称结构化的类型,这种名称比较常见,但是存在区别-对在此讨论区别不感兴趣。


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

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

44
“规则难以记住” <==这是使我无法执行Javascript中任何“有意义”的事情。(以及导致基本calc
练习

9
3 - "5"示例本身就提出了一个很好的观点:即使您专门===用于比较,这也只是变量在Javascript中的工作方式。没有办法完全摆脱它。
贾里特·米拉德

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

94

事实证明,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具有专门为其设计的标志


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

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

4
还要注意的是的Zepto似乎默认==和只使用===在需要它的情况:github.com/madrobby/zepto/blob/master/src/event.js

2
@嘿,坦白地说,Zepto也不是模型代码库-它因使用__proto__而臭名昭著,反过来又几乎单枪匹马地将其强迫成语言规范,以避免破坏移动网站。
本杰明·格伦鲍姆

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

15

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

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

您可以这样定义比较>=(人们通常从>这里开始,但是我发现这比较优雅):

  • 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”来自;)


4
公平地说>=并不是真正的传递。在JS中,也可能没有,a > ba < b没有b == a(例如:)NaN
本杰明·格伦鲍姆

8
@BenjaminGruenbaum:这就像说+不是真正的可交换,因为NaN + 5 == NaN + 5没有成立。关键是要>=使用与数字式值==保持一致的值。毫不奇怪,“不是数字”从本质上说不是数字式的;)
back2dos 2015年

4
因此,的不良行为==与的不良行为是一致的>=吗?太好了,现在我希望有一个>==……
Eldritch Conundrum 2015年

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

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

8

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

==不是自反的

A == A 可能是错误的,例如

NaN == NaN // false

==传递

A == B并且B == 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

这里有什么逻辑可以true结合1吗?公认1是杰出的,但也是如此-1。我个人认为没有任何理由可以转换true1

而且情况变得更糟:

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

所以true确实是1在所有数字之间转换!这合乎逻辑吗?直观吗?答案留给运动;)

但是呢:

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

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

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

至少在数学上-绝对是的!布尔和数字之间的一个简单的等价关系,也可以只进行构建false,并0在同一类之中。因此,false == 0将是唯一的非平凡宽松平等。

字符串呢?

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

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

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

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

' ' == '' // false

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

结论

如果宽松的等式运算符==遵守一些基本的数学定律,它可能会非常有用。但是,令人遗憾的是,它的实用性受到了损害。


NaN呢 另外,除非强制使用特定的数字格式与字符串进行比较,否则必须导致字符串不直观或不传递。
所罗门·乌科

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

7

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

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比as Number(key) === some_number或as 更自然key === String(some_number)


3

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

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

这样可以省去删除0并转换为整数的过程。


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

我不知道您可以这样做。
乔恩·斯诺

7
我得到很多。
乔恩·斯诺

1
@ratbum或+'01' === 1
Eric Lagergren

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

3

我知道这是一个较晚的答案,但是关于null和似乎有些困惑undefined,恕我直言,这是==邪恶的源头,更何况缺乏传递性,这已经够糟了。考虑:

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

这些是什么意思?

  • p1 有名叫“爱丽丝”的主管。
  • p2 有一个主管,名字是“ None”。
  • p3明确地,明确地没有主管
  • p4可能或可能有一名主管。我们不知道,我们不在乎,我们不应该知道(隐私问题?),因为这与我们无关。

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

我知道有些程序员不关心两者之间的差异nullundefined或者选择以不同的方式使用这些术语。如果您的世界没有正确使用null和使用undefined,或者您希望对这些术语做出自己的解释,那就这样吧。我认为这不是一个好主意。

现在顺便说一句,我都没问题,null而且undefined都是虚假的!完全可以说

if (p.supervisor) { ... }

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

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

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

FWIW我知道在==方便的情况下我也遇到过这种情况!就像将字符串输入数字,例如与0进行比较一样。但是,这是hack。作为不正确的世界模型的折衷,您会感到很方便。

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


感谢您展示不同的情况:)但是,您不见了p5……这种情况typeof(p5.supervisor) === typeof(undefined)甚至没有主管的概念:D
TheCatWhisperer
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.