Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
JavaScript Equality Table (dorey.github.io)
160 points by antoinec on Dec 26, 2014 | hide | past | favorite | 73 comments


There is a followup article "Don't Make Javascript Equality Look Worse Than It Is"

"These posts are fundamentally right about == being poorly designed... but they make things look worse by failing to organize the table.... What a mess! But most of the mess is because of the order of values in the table. By grouping equal values together, you get something more sensible"

http://strilanc.com/visualization/2014/03/27/Better-JS-Equal...


Amazing how the same data does make more sense visually when ordered this way but still not compared to ==='s straight line.

The author linked to a jsfiddle that I changed to use the same colors as the original table and improved the legibility a bit which you can find at http://jsfiddle.net/4c5z60qg/ or view as an image here: https://i.imgur.com/assIoqN.png


You can make it look prettier, but it doesn't make the underlying problem any clearer.

For example, few people would immediately be able to know where place the following into the table so that it still looked good:

['Infinity']

'0e1'

['infinity']

'0X0'

[false]

["+0.e4"]

[[[[1.,]]]]

and so on :) (A bit more time, and I could probably come up with even stranger examples).



Simple, and silly. Implicit conversion of string to number is asking for problems.


Do you often find that you have no idea whether your variable contains a string or a number? Either your data comes from user input, an API, or some file-like source. If it's user input, say from an <input>, you would already know to parseInt(). If it's from an API then presumably it's conforming to a documented spec. If it's from a file or something less than conformant, safely parse it.

Why/how would you get to the point where you're operating on the value without being certain of its type?


> Why/how would you get to the point where you're operating on the value without being certain of its type?

My favorite example of this is the twitter API, which used to report retweet count as the string "100+" for the case of over 100 retweets. This behavior was not documented [1], so until you encounter that example, you wouldn't necessarily expect this or defend against a string

[1] From 2012 http://gazit.me/2012/01/09/Twitter-documentation-fail.html


How could === defend you in this case? `"100+" != 100` already.

If you do a typeof before further processing the value then you'll know how to treat it, and additionally someone who comes after you will realize that the value could be a number or a string.


yes, but ``"100+" > 100`` is ``false``


So... you'd have to parseInt anyway?


Bugs happen when more than one programmer interface with each others code and the original intent was not clear enough.


Instead of using === everywhere, if you parse and cast your input to known types you also have the opportunity to catch errors where input is not conforming.

Your parsing makes it obvious to other programmers what your assumptions are about the input. With === all a person will see is that the expected condition did not trigger but no obvious reason why. If `key === 13` fails all we know is that there is no strict equality, but `parseInt(key) == 13` failing means that key is not something which could parse to number 13, and consequently what type(s) key can be is obvious.

Moreover with parseInt the left side of that operator will always be a number or NaN, and the right side is a scalar, so there is no justification for using `parseInt(key) === 13`. It would be preposterous. If you can't trust the return type of parseInt, then your programming environment is unreliable.


I never said a thing about using "===". I think implicit conversion encourages people to use something that is error prone.

Tbh, I would suggest using something like typescript and having more granular types than just "number" and "string".


Exactly, this table just makes it look scary, newbies shouldn't see this ;)


I like the end of the article. "Use three equals unless you fully understand the conversions that take place for two-equals."

Or better said, "Always use 3 equals unless you have a good reason to use 2."


Your rephrasing is better. For example, there's no reason to do:

    typeof someVar === 'number'
...because `typeof` always returns a string, by spec[1]

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


Removing a single character is hardly worth the issues that may arise from not using it. The only quasi-acceptable use of '==' IMHO is:

    myVar == null
as it is a little easier than:

    myVar === undefined || myVar === null
That said, I use only '===' as I don't want to ever worry about my code (plus, the use of a maybe monad makes this and other problems go away).


== vs. === aside, it's a bit hypocritical to use === because it's safer when you're using undefined as a keyword. You can't tell just from looking at that line whether the variable undefined is actually undefined or not; it could, in fact, be defined (below the global scope, that is -- unless you're in an older browser, I suppose).

Granted, defining a variable called undefined would be absurd, and I realize I'm nitpicking here, but if we're trying to make a point about good practice, typeof would be the way to go. (Of course, more often you only really care if something == null or not, so you'd just do a simple == null check, like you said.)


Well, there is a reason: consistency. ;)

I hate seeing "==" in code because it's rarely clear whether the programmer intends it to coerce or just didn't bother with the third keystroke. So "===" is just enforced everywhere through jshint.


Might the === comparison be faster, since the underlying JavaScript engine doesn't need to determine how the two types should be cast back and forward to be compared?


Yep, === will be quicker if the types are the same.


Yes there is a reason, because somebody reading the code is going to have to wonder why the heck you used a double-equals, and the code could be edited later in such a way that the triple-equals becomes relevant again.


It's probably easier to set a linter to disallow "==" equality comparison globally instead of only when "===" is strictly necessary.


I prefer this phrasing too. I've updated the page to make it less confusing...

https://github.com/dorey/JavaScript-Equality-Table/commit/d7...


  if (2) { console.log('yes') }
  > yes
  > undefined
  if (2==true) { console.log('yes') }
  > undefined
I would have thought these would be the same - what's the difference between being "truthy" in the first case, and being ==true, as in the second?


Well I can answer that myself thanks to sheetjs' link to the spec.

Case 1: From the section for 'if ()', http://www.ecma-international.org/ecma-262/5.1/#sec-12.5,

  If ToBoolean(GetValue(exprRef)) is true, ...
new Boolean(2) => true, so 2 is 'truthy' alone inside if ().

Case 2: From the == section, http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3,

  If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
So the boolean is coerced to a number, and new Number(true) => 1. I wonder why they didn't do it the other way around, and make the number a boolean like above. You're losing seemingly useful information with new Number(<boolean>)


`2` is truthy, but `==` forces a coercion, which ends up converting Boolean `true` to a number, which is 1 (`ToNumber(true) := 1` [1]), which is ostensibly not equal to 2.

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


I forget the specifics of Javascript, but in many languages "truthy" (used in ifs) is usually closer to "!= false" than "== true". e.g. any object or non-zero number is truthy (except "" in JS), but only a handful are actually == true or == false.


Good point, same in JS:

  if (2 != false) console.log('yes')
  > yes
false goes to 0 and 2 != 0.


Most of these scenarios are mitigated by knowing what types of data are coming into your functions. If you're defending against a string being compared to a number, I'd sooner wonder why you don't know whether you have a string or a number in your variable in the first place. What could cause you to attempt to use `[1]`(array containing number 1 as an element) as a boolean? Seems like you should know if a variable contains an array and not true/false, and if you don't then that's a better place to focus your attention.

Happy to hear counterexamples though.


If my "number" is in fact a string I'd like to get an error instead of a type conversion that hides this error. This might not necessarily be an ignorant beginner mistake, but may very well be a manifestation of some complicated bug or error on the other end of the program, possibly in a library I haven't studied enough and made assumptions about, or a black-box webservice.

I can handle it but don't see why I would go out of my way to defend Javascript in this regard.


> If my "number" is in fact a string I'd like to get an error instead of a type conversion that hides this error.

If you expect a number or a string, call parseInt() on your value and then isNaN() on that will tell you whether you have an error condition. Your code will be more understandable if you spend time parsing your inpt and explicitly casting to expected types, rather than papering over differences with ===


Wrong jessedhillon, becaue parseInt() is as quirky as == (this is JavaScript, remember!).

parseInt('1 are you joking') returns 1.

parseInt('077') returns 63 on some older browsers (get off my octal lawn).

parseInt('0XALIC ACID') returns 10.

parseInt(' -1e5') returns -1.

And dealing with Integers in JavaScript is fraught with danger since browser JavaScript only really knows floating point numbers. e.g. parseInt('999999999999999999999999999999999999999999999999999999999999999999999999999999999') returns 1e+81...

Smoke that!

Edit: It is possible to build some reliable integer routines using logical operators (since they convert to unsigned integers to perform the operation) but one needs to understand the other underlying quirks to do so.


Yes, it's parseInt() plus some further checks if the results are within an expected range in my code. And overall being more verbose, explicit, and careful. And the occasional squinting eyes from the "Fry meme".


ParseInt accepts a second parameter for the radix, it's use is highly encouraged.


It immediately grabbed my attention that they used images for the column labels. Vertical text should be doable with CSS.



This reminds me of Miłosz Kośmider's "Zeros in JavaScript" comparison table: http://zero.milosz.ca/

Essentially the same thing, but covers a few more operations.


All dynamically typed languages with implicit coercion will have similar-looking tables:

PHP: http://www.blueshoes.org/en/developer/php_cheat_sheet

Perl: http://qntm.org/equality

Python: http://i.stack.imgur.com/Ya0Ux.png (I don't think all the entries are correct)


> Python: http://i.stack.imgur.com/Ya0Ux.png (I don't think all the entries are correct

Python checks out;

    >>> b=[True,False,1,0,-1,"","True","False","1","0","-1",None,[],{},[[]],[0],[1]]
    >>> for x in b:
    ...     print [int(y == x) for y in b]
    ... 
    [1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    [0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    [1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    [0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]


I see the problem - the empty string is in a different position between the rows and columns, leading to "" being equal to the string "True", which is false.


It's part of the official docs: http://php.net/manual/en/types.comparisons.php


Object plus object is not a number :)

https://www.destroyallsoftware.com/talks/wat


That's because the first 'object' is a block instead of an object literal.


For another surprise, try assigning those empty objects to variables before adding. By itself,

{}+{}

doesn't mean what you might think it means.


Interesting, I didn't know about `[1] == true`, `[0] == false` family of equalities.

Edit: it seems more general, `42 == [42]` etc. holds.

Edit: more fun

  ([0])==false // true
 !([0])==false // true


I am not sure what's up with the first two. But in the latter one, I believe the array is being coerced to a string, which is its contents separated by commas, so in this case just "42". Then either the left-hand 42 is coerced to a string or the right-hand "42" is coerced to a number, I'm honestly not sure. It hasn't been all that long since I read the spec about this, either. Sheesh.


Translation:

    "0"   == 0
    false == false


Well, at least it's got some symmetry to it!


That's actually pretty important: it means that == and != are commutative. Imagine the "wat" if that wasn't the case either. And hey, it's JS, it could've been.


Commutative but not transitive, as in the example

    var x = {toString:function() { return "foo"; } }, y = "foo", z = new String("foo")
x == y and y == z but x != z


This implies there is no equivalence relation with == in JS. In order to form an equivalence relation, it would need to be reflexive, symmetric and transitive.


Many such examples can be spotted in the table:

[] == 0 == [[]]

[1] == true == [1]

...

There are even two examples with self-equality (x == x && y == y && z == z):

"0" == false == ""

"0" == 0 == ""


"[] == []" and also with "===" is false. Why they choose not to use these for array comparisons?


They are. What do you mean?


It's useless since it's always false (unless you are comparing the same variable).

> [1,2,3] == [1,2,3]

false


Going to the link made my Firefox (34.0.5) allocate over 2 GB of RAM... and then everything stalled to molasses...


Can someone do this table for Go? I'm just curious to see it.


It would likely just be the diagonal line in the middle, i.e. the === table.


[flagged]


Your conviction and intelligence definitely show from your decision to use a throwaway to reply!

Out of curiosity, what should people be using instead of Go? (And don't say Haskell, because surely you're more pragmatic than that.)

I'm looking at Elixir now, but Go has a growing community and great concurrency.

It seems to me that you perhaps hate any language that encourage or forces OO design, even though almost no one does real, widely-used, functional projects.


> don't say Haskell, because surely you're more pragmatic than that

What's that supposed to mean?


For real projects (ones that definitely involve other people and probably involve money in some way), most of your wasted time is not due to the language you're using.

Sure, you could be using non-strict JS or PHP and get bogged down because your typos are silently ignored, but lots of IDEs have static analysis and whatnot to prevent that. Any popular language has workarounds for its most time-wasting issues.

Where lots of time is actually lost is 1) learning and 2) reinventing the wheel.

Imagine, for example, that there's a magical language called AFP (Absolutely Fucking Perfect). It's perfect. Everything you write in it is short, concise, readable, maintainable, and compiles to hyper-efficient code for any number of cores.

Unfortunately, AFP is only used by 1,000 people. See the problem here? The time you spend learning is going to be astronomical, because you won't find the tutorials, Stack Overflow questions, books, forum posts, etc. that you find with a popular language.

And then, perhaps worse, you don't have many libraries. None of those other 1,000 people were working with REST APIs? That sucks, you have to write a module for that. None of them needed to process images? Same issue.

With AFP, you're likely looking at writing 10 times the amount of code that you would in a popular pile of garbage like PHP or JS.

Haskell is AFP. It's widely considered to be a great language, but it's just not practical. There's hardly anything built in Haskell out there for you to build your own stuff on.

We're now getting into an era where we might have the best of both worlds. Rust seems to be really interesting on that front. I've also seen the same argument for Scala, Go, and Elixir. But Haskell is not one of those frontrunner languages to take over from the slapped-together old guard of Perl, PHP, Python, and JS.


> There's hardly anything built in Haskell out there for you to build your own stuff on.

False. How do you come to this conclusion.

I agree with your overall point mostly, but it doesn't apply to Haskell.


Yes, it does. Haskell is mostly used for small, single-purpose tools, or it's used to teach FP principles.

The number of repos on Github for Haskell is absolutely minuscule compared to every mainstream language.

There just aren't that many people using Haskell for real projects, nor are there lots of practical libraries for it. (You're free to name some examples if you disagree...)

Perhaps worse, Haskell is hard to learn. Sure your code is super-optimized for a compiler or whatever, but the learning curve is so high that you have to find someone else that's experienced in Haskell to work on your project. That's very, very hard.


> Yes, it does.

Nope.

> Haskell is mostly used for small, single-purpose tools, or it's used to teach FP principles.

According to whom? If you, how do you come to that conclusion?

> There just aren't that many people using Haskell for real projects, nor are there lots of practical libraries for it. (You're free to name some examples if you disagree...)

I could name many, though I'm not sure they would meet your definition of practical. Could you give me your definition of a practical library, and/or perhaps even better a few practical libraries from your language of choice?

> Perhaps worse, Haskell is hard to learn.

Haskell is no harder to learn than your first programming language where you also had to learn imperative programming semantics and things like Object Oriented programming.

> Sure your code is super-optimized for a compiler or whatever,

It really seems like you don't know much about Haskell. I think you'd be doing yourself a service to take a look.

> but the learning curve is so high that you have to find someone else that's experienced in Haskell to work on your project. That's very, very hard.

Not if you offer remote work and post it to /r/haskell, haskellers.com, and functionaljobs.com

Getting local candidates with previous Haskell experience could be more difficult outside of places like NYC, Austin, Silicon Valley, etc.

I wouldn't advise using it for a start-up unless you have at least one person that is moderately experienced and at the very least created a few libraries.


There are less than 5k Haskell users on Github, and they've released 30k repos.

By comparison, Go has 9k users and 50k repos. And a lot of people say that Go is not that useful of a language because the community is small.

What I'm saying isn't particularly controversial. There's a long thread here where Haskell is brought up frequently in the same context I'm talking about it: https://news.ycombinator.com/item?id=8802454

My definition of practical is this: people use it in practice. They use it for business or for personal projects that other people get use out of.

The core purpose of software is to automate things that, otherwise, people would have to do. What large jobs is Haskell currently automating for people?


> By comparison, Go has 9k users and 50k repos. And a lot of people say that Go is not that useful of a language because the community is small.

Some people call Python too small. Some even claim "Haskell is mostly used for small, single-purpose tools, or it's used to teach FP principles". Some people say a lot of things, that doesn't make it true.

> My definition of practical is this: people use it in practice. They use it for business or for personal projects that other people get use out of.

I use Haskell for freelance clients currently.

> The core purpose of software is to automate things that, otherwise, people would have to do. What large jobs is Haskell currently automating for people?

https://www.haskell.org/haskellwiki/Haskell_in_industry

Standard Chartered is particularly of note, as you can see in this quora question. You can also see other examples of Haskell automating jobs for people:

http://www.quora.com/What-is-the-largest-commercial-program-...


I don't mind OO / java, I like ruby, I'm hoping rust will be the new language worth using.

I strongly dislike Go for a variety of reasons, mostly dealing with its anti-OO sentiments actually. It has things like "for x := range myArr" but you can't implement that yourself because you actually can't implement either a single and multiple return or implement an iterator/range. The language is full of crap like that, where the programmer is treated like a baby who shouldn't have any power.

Similarly, the language has first class functions, but no functional constructs (map, etc). It doesn't have pattern matching or tuples so multiple-returns feel like a hack. Its error checking is atrocious.

It has no version control or package management worth shit; npm, bundler, etc, all have an idea of "I want something less than this version". In golang, it's worse than C even. They encourage go get, aka "pull from master, surely it won't break" or vendoring. At least in C, you have os package managers to do some of this and pkg-config.

Go is just a terribly designed language in almost every aspect in my opinion. It isn't functional and sucks at OO. It sucks at low level. It sucks at high level. It sucks at large project. It's okay until you need to implement your own type and realize you're a second class citizen.

Hopefully that made sense to you; thanks for asking nicely.


tl;dr ignore == and !=, use === and !== instead


i think this table was already posted many times - but this equality table thing never gets boring :D


It leaves out negative zero.


At least it is commutative.


dynamic typing good

weak typing bad




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: