291

In JavaScript, the Good Parts, Douglas Crockford wrote:

JavaScript has two sets of equality operators: === and !==, and their evil twins == and !=. The good ones work the way you would expect. If the two operands are of the same type and have the same value, then === produces true and !== produces false. The evil twins do the right thing when the operands are of the same type, but if they are of different types, they attempt to coerce the values. The rules by which they do that are complicated and unmemorable. These are some of the interesting cases:

'' == '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

The lack of transitivity is alarming. My advice is to never use the evil twins. Instead, always use === and !==. All of the comparisons just shown produce false with the === operator.

Given this unequivocal observation, is there ever a time when using == might actually be appropriate?

Peter Mortensen
  • 1,050
  • 2
  • 12
  • 14
Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
  • 12
    It *makes sense* in lots of places. Any time it's obvious you're comparing two things of the same type (this happens a lot in my experience), == vs === just comes down to preference. Other times you actually *want* abstract comparison (like the case you mention in your answer). Whether it's *appropriate* depends on conventions for any given project. – Hey Jan 06 '15 at 02:09
  • 3
    @Hey: That's a little vague. If it doesn't matter, then why not use `===` and eliminate any possibility of ambiguity? Also, Stack Exchange questions are never answered with a simple "yes" or "no," and I really doubt that it makes sense in *a lot of places.* – Robert Harvey Jan 06 '15 at 04:00
  • Crockford also recommends not using `++` or `--`. The argument there is the same; use `+=` or `-=` instead to remove any possibility of ambiguity. You can probably guess how often projects follow those guidelines. Does that mean `--` and `++` are never appropriate? I think it just means they are not appropriate for projects following that particular convention. – Hey Jan 06 '15 at 06:26
  • 4
    Regarding "lots of places," in my experience the cases where it doesn't matter outnumber cases where it does. Your experience may be different; maybe we have experience with different kinds of projects. When I look at projects that use `==` by default, `===` stands out and lets me know something important is going on. – Hey Jan 06 '15 at 06:28
  • 3
    @Roy: Which makes sense in *which* case, exactly? (ducks and runs for cover...) – DevSolar Jan 06 '15 at 13:21
  • 4
    I don't think JavaScript goes far enough with it's type coercion. It should have even more type coercion options, just like the [BS](https://skillsmatter.com/skillscasts/6088-the-worst-programming-language-ever) language. – Mark Booth Jan 06 '15 at 14:18
  • 5
    One place I use == is when comparing dropdown id's (which are always char) to model id's (which are commonly int). – Scottie Jan 06 '15 at 15:22
  • 12
    @DevSolar Web development makes sense when you don't want to have to deal with producing a native app for each of 15 platforms as well as certification on the monopoly App Store of each platform that has one. – Damian Yerrick Jan 06 '15 at 17:51
  • You said that rules of equality in javascript is unmemorable but a couple of years ago, I found this post http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/ that make very clear the process of equality in javascript. When you understand the base of the rules, everything seems to be much simpler. In fact, in javascript, when you use equality, javascript try to cast both sides in number. Now, you just have to remember how values that are not numbers are converted to number. Angus Croll also talk about boolean expression which use other rules. We like Javascript! :) – Samuel Jan 07 '15 at 01:19
  • 1
    @tepples There are better options out there, e.g. Xamarin. As a developer I prefer monopolies of stores to monopolies of languages. – Den Jan 07 '15 at 09:44
  • 1
    Personally I would say a decent programmer should get the same functionality when using `==` vs `===` because they have converted their types before comparisons and do not rely on any conversions that `==` would do. I would say that if people use `==` they just need to be careful and check their work. I personally use it because most languages use `==`, plus you save a character. – Spencer Wieczorek Jan 07 '15 at 17:02
  • @Den Web beats Xamarin when a platform's developer program rejects you as a developer for not having enough verifiable industry experience or for operating out of a home office. – Damian Yerrick Jan 07 '15 at 17:16
  • 2
    @tepples: Who does that? Certainly not Apple. Programmers are not required to submit a resume with references, nor are their offices inspected first; they evaluate programs submitted to the Apple store on their own merits. – Robert Harvey Jan 07 '15 at 17:17
  • @RobertHarvey Let's take this to [a chat room](http://chat.stackexchange.com/rooms/20058/do-web-apps-make-sense) – Damian Yerrick Jan 07 '15 at 17:28
  • 4
    [A picture says a thousand words.](https://dorey.github.io/JavaScript-Equality-Table) – Slade Jan 07 '15 at 21:15
  • 1
    @SpencerWieczorek I agree. It's especially true for projects using type annotations extensively; the tooling would warn you if something could be a different type than you expected. The abstract equality operator is idiomatic to the language, consistent with other language features, and recognizable to those coming from other languages. Crockford's recommendation is by no means the only "appropriate" path to follow. Each project should follow whatever conventions are appropriate for it, and contributors should adhere to whatever conventions a given project uses. – Hey Jan 08 '15 at 16:18

7 Answers7

246

I'm going to make an argument for ==

Douglas Crockford which you cited is known for his many and often very useful opinions. While I'm with Crockford in this particular case it's worth mentioning it is not the only opinion. There are others like language creator Brendan Eich who don't see the big problem with ==. The argument goes a little like the following:

JavaScript is a behaviorally* typed language. Things are treated based on what they can do and not their actual type. This is why you can call an array's .map method on a NodeList or on a jQuery selection set. It's also why you can do 3 - "5" and get something meaningful back - because "5" can act like a number.

When you perform a == equality you are comparing the contents of a variable rather than its type. Here are some cases where this is useful:

  • Reading a number from the user - read the .value of an input element in the DOM? No problem! You don't have to start casting it or worrying about its type - you can == it right away to numbers and get something meaningful back.
  • Need to check for the "existence" of a declared variable? - you can == null it since behaviorally null represents there is nothing there and undefined doesn't have anything there either.
  • Need to check if you got meaningful input from a user? - check if the input is false with the == argument, it will treat cases the user has entered nothing or just white-space for you which is probably what you need.

Let's look at Crockford's examples and explain them behaviorally:

'' == '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 

Basically, == is designed to work based on how primitives behave in JavaScript, not based on what they are. While I don't personally agree with this point of view there is definitely merit in doing it - especially if you take this paradigm of treating types based on behavior language-wide.

* some might prefer the name structural typing which is more common but there is a difference - not really interested in discussing the difference here.

Benjamin Gruenbaum
  • 5,157
  • 4
  • 24
  • 34
  • 8
    This is a great answer, and I use all three of your 'for ==' use cases. #1 & #3 are especially useful. – Chris Cirefice Jan 06 '15 at 07:06
  • 237
    The problem with `==` isn't that none of its comparisons are *useful*, it's that the rules are *impossible to remember* so you are nearly guaranteed to make mistakes. For example: "Need to check if you got meaningful input from a user?", but '0' is a meaningful input and `'0'==false` is true. If you had used `===` you would have had to explicitly think about that and you wouldn't have made the mistake. – Timmmm Jan 06 '15 at 12:03
  • 44
    "the rules are impossible to remember" <== this is the one thing that scares me away from doing anything "meaningful" in Javascript. (and float math that lead to problems in basic calc excercises) – WernerCD Jan 06 '15 at 14:37
  • 10
    The `3 - "5"` example raises a good point on its own: even if you exclusively use `===` for comparison, that's just the way variables work in Javascript. There's no way to escape it completely. – Jarett Millard Jan 06 '15 at 14:56
  • 25
    @Timmmm note I am playing devil's advocate here. I don't use `==` in my own code, I find it an anti-pattern and I completely agree the abstract equality algorithm is hard to remember. What I'm doing here is making the argument people make for it. – Benjamin Gruenbaum Jan 06 '15 at 14:59
  • 1
    @WernerCD floating math is a definite pain point in javascript but I have a hard time wrapping my head around professional programmers hating/refusing to write javascript when most of it's pitfalls can easily be avoided. The `==` equality comparison map is hard to remember so just use `===` everywhere. Don't want to worry about the other pitfalls, run a common lint tool. – pllee Jan 06 '15 at 15:00
  • @pllee It's not really that I "refuse". It's more that I'll use tools that I feel don't have those issues (ie: Typescript... or the other "better" languages can "compile down" to JS) - as well as languages that I'm more comfortable in (like C# on the server instead of Node). When you add silliness like `==` rules, floating point (which can definitely be worked around) and other weirdness - I've just found it easier to use other tools. You can most definitely succeed with JS end-to-end, I just can't move past these pain points. My 2c. – WernerCD Jan 06 '15 at 16:07
  • @WernerCD fair enough those are all valid concerns. I think that when you avoid doing the "bad" stuff which should be easy to do you are left with a good language underneath that many don't give much of a chance. – pllee Jan 06 '15 at 16:24
  • 4
    Maybe I'm a bad person. I tend to use `==` for the reasons above, and because generally I'd prefer the values to try and be equal if they are different types. I use `===` as more of an exception where I know there may be different types which I don't want to be equal. – DoubleDouble Jan 06 '15 at 17:22
  • 1
    Excellent... This is the kind of answer I was after. – Robert Harvey Jan 06 '15 at 18:18
  • 2
    I also want to throw my two cents in and say that anytime you do an `if (someVar)`, you are using the `==`. – ps2goat Jan 06 '15 at 20:06
  • 2
    @ps2goat No, you don't. `'0' == true` is false, `!!'0'` is true. – Nikolai Jan 07 '15 at 10:30
  • 3
    Duck typing doesn't "just happen." If "5" can act like a number, that's just because the language designers decided to implement "acting like a number" for the thing that is "5". In other languages "5" cannot act like a number (and many people think that's fantastic). Also, *how* "5" can act like a number is also up for debate. Should "5" map to the Platonic integer 5? Or should it map to some ordinal character code that represents the string "5" in a lookup table of characters? Which one of those ways of "acting like a number" is "right"? Why pick just one way as a forced convention? –  Jan 07 '15 at 13:41
  • @prpl.mnky.dshwshr I completely agree with that. This post represents an opinion I do not hold but definitely exists and I believe deserves to be heard here. The argument people who like this sort of weak typing make is that it's _obvious_ that "5" should act like the number 5 (what other number would the average person expect it to act like?). – Benjamin Gruenbaum Jan 07 '15 at 15:01
  • @Nikolai, but if you do an `if (console)` in IE, for example, it checks to see if the console object exists. That checks for a falsey condition, which means it isn't doing type checking (which `===` does). (In IE 9, `console` only exists if the dev tools are open, for example.) truthy and falsey condition checks are great examples of when you should use `==` instead of `===`, whether implicitly, as shown above, or explicitly. – ps2goat Jan 07 '15 at 15:42
  • 4
    @ps2goat: `if (console)` is equivalent to `if (Boolean(console))`. Which is very different from `if (console == true)` which is essentially `if (Number(console) == Number(true))`. **Converting** a value to a Boolean is very different from **comparing** a value to a Boolean. – Felix Jan 07 '15 at 17:11
  • "When you perform a == equality you are comparing the contents of a variable rather than its type." It's not the main point of this post, but an important nit to pick (because the confusion between variables and values seems to be widespread, sometimes). This has nothing to do with variables. An expression like **'0' == true** doesn't have any variables. – Joshua Taylor Jan 07 '15 at 18:50
  • @JoshuaTaylor because [`EqualityExpression`](http://es5.github.io/#x11.9) (what the left hand side and right hand side are both called) is a term everyone in the world knows and loves :) – Benjamin Gruenbaum Jan 07 '15 at 20:19
  • 1
    in the first case you consider `'0'` as being `got input from user`, in the 4th case you add that `input from user (...) is truthy` and in the 5th, `'0'` is no longer `input from user`, it is now `substitute for 0`... – njzk2 Jan 08 '15 at 21:27
  • When you say call, you are referring to `Function.prototype.call()` right? At first it seemed a little misleading, because you can't simply use `.map()` on a nodeList. `document.querySelectorAll("div").map(function(){});` will cause an error. If you meant `Array.prototype.map.call(document.querySelectorAll("div"),function(){});` perhaps you could clarify that a little bit. – Travis J Jan 09 '15 at 00:42
  • @BenjaminGruenbaum But even EqualityExpression is talking about the grammar of the language, and variables can appear in an equality expression. The point here, though, is about the *semantics*; the things being compared are not the expressions, but the *values*. – Joshua Taylor Jan 10 '15 at 16:34
  • A typical example how the content comparison fails is shown by `[1,2] == [1,2]` being `false` – Dmitri Zaitsev May 16 '16 at 14:06
103

It turns out that jQuery uses the construct

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

extensively, as a shorthand for the equivalent code:

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

This is a consequence of the ECMAScript Language Specification § 11.9.3, The Abstract Equality Comparison Algorithm, which states, among other things, that

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.

and

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

This particular technique is common enough that JSHint has a flag specifically designed for it.

CodesInChaos
  • 5,697
  • 4
  • 19
  • 26
Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
  • 12
    No fair answering your own question I wanted to answer this :) `== null or undefined` is the only place where I don't use `===` or `!==` – pllee Jan 05 '15 at 22:40
  • 30
    To be fair jQuery is hardly a model codebase. Having read the jQuery source several times it's one of my least favorite codebases with a lot of nested ternaries, unclear bits, nesting and things I would otherwise avoid in real code. Don't take my word for it though - read it https://github.com/jquery/jquery/tree/master/src and then compare it with Zepto which is a jQuery clone: https://github.com/madrobby/zepto/tree/master/src – Benjamin Gruenbaum Jan 06 '15 at 07:18
  • 5
    Also note that Zepto seems to default to `==` and only uses `===` in cases where it's needed: https://github.com/madrobby/zepto/blob/master/src/event.js – Hey Jan 06 '15 at 07:24
  • 3
    @Hey to be fair Zepto is hardly a model codebase either - it's infamous for using `__proto__` and in turn forcing it almost single handedly into the language specification to avoid breaking mobile websites. – Benjamin Gruenbaum Jan 06 '15 at 09:07
  • 2
    @BenjaminGruenbaum that wasn't a judgement call on the quality of their codebase, just pointing out that different projects follow different conventions. – Hey Jan 06 '15 at 19:57
  • 1
    @benjamin Although, to be fair to JQuery, a lot of that code isn't written to be proper or sexy, but to be compatible with the most amount of browsers. – AlexanderJohannesen Jan 11 '15 at 19:19
  • @pilee I'm curious why you said comparing with `null` and `undefined` is the only place you don't use `===` or `!==`. In my opinion the mixing of `null` and `undefined` is biggest reason for avoiding `==`. In fact, if I were to think about places where I would succumb to using `==`, then I might say "anywhere that I know that I won't get caught accidentally mixing up `null` and `undefined`." :) Cheers. – Ray Toal Jan 12 '15 at 03:08
  • CoffeeScript’s `?` (existence) operator uses `==`, and that’s the only place that CoffeeScript uses `==`. So `if options?` becomes `if (options != null)`, where the point is to take advantage of the fact that `!= null` also checks against `undefined`, `false`, etc. (`unless options?` would use `==` instead of `!=`.) – Geoffrey Booth Apr 30 '18 at 07:43
  • It sounds like you're saying that equivalence of the two tests follows from the spec passages you quoted, but it doesn't follow. – Don Hatch Jun 17 '18 at 02:16
17

Checking values for null or undefined is one thing, as has been explained abundantly.

There's another thing, where == shines:

You can define comparison from >= like so (people usually start from > but I find this more elegant):

  • a > b <=> a >= b && !(b >= a)
  • a == b <=> a >= b && b >= a
  • a < b and a <= b are left as an exercise to the reader.

As we know, in JavaScript "3" >= 3 and "3" <= 3, from which you get 3 == "3". You can make a point that it's a horrible idea to allow implementing comparison between strings and numbers by parsing the string. But given that this is the way it works, == is absolutely the correct way to implement that relationship operator.

So the really good thing about == is that it is consistent with all other relationships. To put it differently, if you write this:

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

You are implicitly using == already.

Now to the quite related question of: Was it a bad choice to implement number and string comparison the way it is implemented? Seen in isolation, it seems a rather stupid thing to do. But in the context of other parts of JavaScript and the DOM, it's relatively pragmatic, considering that:

  • attributes are always strings
  • keys are always strings (the use case being that you use an Object to have a sparse map from ints to values)
  • user input and form control values are always strings (even if the source matches input[type=number])

For a whole number of reasons it made sense to make strings behave like numbers when needed. And assuming that string comparison and string concatenation had different operators (e.g. :: for concating and a method for comparing (where you can use all kinds of parameters regarding case sensitivity and what not)), this would in fact be less of a mess. But this operator overloading is probably in fact where the "Java" in "JavaScript" comes from ;)

back2dos
  • 29,980
  • 3
  • 73
  • 114
  • 4
    To be fair `>=` is not really transitive. It's quite possible in JS that neither `a > b` nor `a < b` nor `b == a` (for example: `NaN`). – Benjamin Gruenbaum Jan 06 '15 at 16:51
  • 8
    @BenjaminGruenbaum: That's like saying `+` is not really commutative, because `NaN + 5 == NaN + 5` doesn't hold. The point is that `>=` works with number-ish values for which `==` works consistently. It shouldn't be surprising that "not a number" is by its very nature not number-ish ;) – back2dos Jan 06 '15 at 22:13
  • 4
    So the poor behavior of `==` is consistent with the poor behavior of `>=` ? Great, now I wish there was a `>==`... – Eldritch Conundrum Jan 07 '15 at 13:41
  • 2
    @EldritchConundrum: As I have tried to explain, the behavior of `>=` is rather consistent with the rest of the language/standard APIs. In its totality, JavaScript manages to be more than the sum of it's quirky parts. If you'd like a `>==`, would you also want a strict `+`? In practice, many of these decisions make many things a lot easier. So I wouldn't rush to judging them as poor. – back2dos Jan 07 '15 at 13:55
  • Consistency throughout the language is not the problem here, remembering the magic rules is. The rules for `<` are strange, see http://strilanc.com/visualization/2014/03/27/Better-JS-Equality-Table.html Among programming languages, having the `+` operator work on non-number types is a more common abuse, and a less harmful one IMHO. – Eldritch Conundrum Jan 07 '15 at 16:32
  • I'm certainly no javascript expert, so I won't weigh in on the meat of this argument, but I want to point out that describing behavior as being "rather consistent with the rest of the language" hardly seems a point in favor of the behavior. Seems more like [damning with faint praise](http://en.wikipedia.org/wiki/Damning_with_faint_praise). – Eric King Jan 07 '15 at 16:57
  • 2
    @EldritchConundrum: Again: relationship operators are meant to compare numerical values, where one operand may in fact be a string. For operand types for which `>=` is meaningful, `==` is equally meaningful - that's all. Nobody says you should compare `[[]]` with `false`. In languages like C the result of this level of nonsense is undefined behavior. Just treat it the same way: don't do it. And you'll be fine. You also won't be needing to remember any magic rules. And then it's actually rather straight forward. – back2dos Jan 07 '15 at 18:03
  • 2
    @EricKing: I disagree. It's not that I like JavaScript. I much prefer to use statically typed languages, where static analysis forces you to ensure the operands have sensible types. But if you make it a design philosophy that your language should be forgiving by automatically coercing things at runtime as needed, you can't do it everywhere except with the `==` operator. The result is would be unusable. Given certain axiomatic design decision, this is sensible behavior. Dismissing that seems like a [perfect solution fallacy](http://bit.ly/1wCt5Sr). – back2dos Jan 07 '15 at 18:13
  • @back2dos I do agree with you that the behavior of `>` is consistent with the horrible initial decision. That is why a `>==` operator would be useful just like `===` is. Since I'm not using `==` because it doesn't behave like I want it to, I'd rather not use `>=` either, to be consistent. – Eldritch Conundrum Jan 08 '15 at 11:20
  • @back2dos, I agree with your sentiment, but it indeed means that addition in javascript is not always commutative, which is a very important (albeit rarely noticed) thing to be aware of. Python, for instance, has an [__radd__](https://docs.python.org/2/reference/datamodel.html#object.__radd___) method that allows one to override such occurrences in your own classes and reimplement commutativity with other types where needed. – yoniLavi Jan 08 '15 at 17:26
  • 1
    @EldritchConundrum `x === y` is just a shortcut for `(typeof x == typeof y && x == y)`. So `>==` would just be a shortcut for `(typeof x == typeof y && x >= y)`. I'll also note that > and < also don't check for type, so if you're worried about `'5' < 6` being true, you'll need to compare type there as well. – trlkly Jan 11 '15 at 22:15
10

As a professional mathematician I see in Javscript's sameness operator == (also called "abstract comparison", "loose equality") an attempt to build an equivalence relation between entities, which includes being reflexive, symmetric and transitive. Unfortunately, two of these three fundamental properties fail:

== is not reflexive:

A == A may be false, e.g.

NaN == NaN // false

== is not transitive:

A == B and B == C together do not imply A == C, e.g.

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

Only symmetric property survives:

A == B implies B == A, which violation is probably unthinkable in any case and would lead to serious rebellion ;)

Why equivalence relations matter?

Because that is the most important and prevalent type of relation, supported by numerous examples and applications. The most important application is decomposition of entities into equivalence classes, which is itself a very convenient and intuitive way of understanding relations. And failure to be equivalence leads to the lack of equivalence classes, which in turn leads to the lack of intuitiveness and unnecessary complexity that is well-known.

Why is it such a terrible idea to write == for a non-equivalence relation?

Because it breaks our familiarity and intuition, as literally any interesting relation of similarity, equality, congruence, isomorphism, identity etc is an equivalence.

Type conversion

Instead of relying on an intuitive equivalence, JavaScript introduce type conversion:

The equality operator converts the operands if they are not of the same type, then applies strict comparison.

But how is the type conversion defined? Via a set of complicated rules with numerous exceptions?

Attempt to build equivalence relation

Booleans. Clearly true and false are not same and should be in different classes.

Numbers. Luckily, the equality of numbers is already well-defined, in which two different numbers are never in the same equivalence class. In mathematics, that is. In JavaScript the notion of number is somewhat deformed via presence of the more exotic -0, Infinity and -Infinity. Our mathematical intuition dictates that 0 and -0 should be in the same class (in fact -0 === 0 is true), whereas each of infinities is a separate class.

Numbers and Booleans. Given the number classes, where do we put booleans? false becomes similar to 0, whereas true becomes similar to 1 but no other number:

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

Is there any logic here to put true together with 1? Admittedly 1 is distinguished, but so is -1. I personally don't see any reason to convert true to 1.

And it gets even worse:

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

So true is indeed converted into 1 among all numbers! Is it logical? Is it intuitive? The answer is left as exercise ;)

But what about this:

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

The only boolean x with x && true being true is x = true. Which proves that both 1 and 2 (and any other number than 0) convert to true! What it shows is that our conversion fails another important property -- being bijection. Meaning that two different entities can convert to the same one. Which, by itself, does not have to be a big problem. The big problem arises when we use this conversion to describe a relation of "sameness" or "loose equality" of whatever we want to call it. But one thing is clear -- it is not going to be an equivalence relation and it is not going to be intuitively described via equivalence classes.

But can we do better?

At least mathematically -- definitely yes! A simple equivalence relation among booleans and numbers could be constructed with only false and 0 being in the same class. So false == 0 would be the only non-trivial loose equality.

What about strings?

We can trim strings from whitespaces at the beginning and the end to convert to numbers, also we can ignore zeroes in front:

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

So we get a simple rule for a string -- trim the whitespaces and zeroes in front. Either we get a number or empty string, in which case we convert to that number or zero. Or we don't get a number, in which case we don't convert and so get no new relation.

This way we could actually get a perfect equivalence relation on the total set of booleans, numbers and strings! Except that ... JavaScript designers obviously have another opinion:

' ' == '' // false

So the two strings that both convert to 0 are suddenly non-similar! Why or why? According to the rule, strings are loosely equal precisely when they are strictly equal! Not only this rule breaks the transitivity as we see, but also it is redundant! What is the point of creating another operator == to make it strictly identical with the other one ===?

Conclusion

The loose equality operator == could have been very useful if it were abiding to some fundamental mathematical laws. But as it sadly doesn't, its usefulness suffers.

Dmitri Zaitsev
  • 321
  • 3
  • 9
  • 1
    What about `NaN`? Also, unless a specific number format is enforced for comparison with strings, either unintuitive string comparison or non-transitivity must result. – Solomon Ucko May 31 '19 at 19:56
  • 1
    @SolomonUcko `NaN` acts as bad citizen :-). Transivity can and should be upheld for any equivalence comparison, intuitive or not. – Dmitri Zaitsev Jun 01 '19 at 03:16
7

Yes, I've run across a use case for it, namely when you're comparing a key with a numerical value:

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

I think it's a lot more natural to perform the comparison as key == some_number rather than as Number(key) === some_number or as key === String(some_number).

user541686
  • 8,074
  • 8
  • 38
  • 49
3

I ran across a pretty useful application today. If you want to compare padded numbers, like 01 to normal integers, == works just fine. For example:

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

It saves you removing the 0 and converting to an integer.

Jon Snow
  • 139
  • 2
3

I know this is a late answer, but there seems to be some possible confusion about null and undefined, which IMHO is what makes == evil, more so that the lack of transitivity, which is bad enough. Consider:

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

What do these mean?

  • p1 has a supervisor whose name is "Alice."
  • p2 has a supervisor whose name is "None."
  • p3 explicitly, unequivocally, does not have a supervisor.
  • p4 may or may have a supervisor. We don't know, we don't care, we're not supposed to know (privacy issue?), as in it's none of our business.

When you use == you are conflating null and undefined which is wholly improper. The two terms mean completely different things! Saying that I don't have a supervisor simply because I refused to tell you who my supervisor is is wrong!

I understand there are programmers who don't care about this difference between null and undefined or choose to use these terms differently. And if your world does not use null and undefined correctly, or you wish to give your own interpretation to these terms, so be it. I don't think that's a good idea though.

Now by the way I have no problem with null and undefined both being falsy! It is perfectly okay to say

if (p.supervisor) { ... }

and then null and undefined would cause the code that processes the supervisor to be skipped. That is correct, because we don't know or don't have a supervisor. All good. But the two situations are not equal. This is why == is wrong. Again, things can be falsy and used in a duck typing sense, which is great for dynamic languages. It is proper JavaScript, Pythonic, Rubyish, etc. But again, these things are NOT equal.

And don't get me started on non-transitivity: "0x16" == 10, 10 == "10" but not "10" == "0x16". Yes, JavaScript is weakly types. Yes, it is coercive. But coerciveness should never, ever, apply to equality.

By the way, Crockford does have strong opinions. But you know what? He is correct here!

FWIW I understand that there are, and I have personally run into, situations where == is convenient! Like taking string input for numbers and, say, comparing to 0. However, this is hack. You have convenience as a tradeoff for an inaccurate model of the world.

TL;DR: falsiness is a great concept. It should not extend to equality.

Ray Toal
  • 1,305
  • 8
  • 11
  • Thank you for showing the different situations :) However, you are missing `p5`... the one situation of `typeof(p5.supervisor) === typeof(undefined)` where supervisor does not even exist as a concept :D – TheCatWhisperer Feb 01 '18 at 20:26