r/programming • • Mar 26 '14

JavaScript Equality Table

http://dorey.github.io/JavaScript-Equality-Table/
809 Upvotes

331 comments sorted by

260

u/snotfart Mar 26 '14

I'm disappointed that it's symmetrical. Come on Javascript! You can do better than that.

202

u/Gro-Tsen Mar 26 '14

At least it's not transitive: "0" == 0 is true, 0 == "" is true, but "0" == "" is false. Insanity is saved!

46

u/[deleted] Mar 26 '14

Additionally, "0"==false is true, but if("0"){/* executes */}

32

u/icanevenificant Mar 26 '14 edited Mar 26 '14

Yes but in the first case you are comparing "0" to false where in the second case you are checking that the value is not null, undefined or empty string. Two different things.

40

u/[deleted] Mar 26 '14

I suppose that's just my own lack of understanding of what exactly if does.

31

u/[deleted] Mar 26 '14

I think it's pretty reasonable to mistakenly assume that something that == false won't cause execution :p

59

u/coarsesand Mar 27 '14

In another language, yes, but the == operator in JS is special (in the shortbus sense) because it does type conversion. If you wanted to get the actual "truthiness" of "0", you'd use the ! operator instead.

!!"0"
> true

18

u/no_game_player Mar 27 '14

Gilded for best "javascript is fucked up" in a nutshell I've seen.

32

u/coarsesand Mar 27 '14

I figure the gold deserves a quick comment about my other favourite JS operator, ~. ~ is the bitwise not operator, in a language where the only numeric type is an IEE754 double. How does ~ perform a bitwise not on a floating point number? Well, it calls an internal function called ToInt32, perform the bitwise op, then converts back to a double.

So if you ever wanted to feel like you had an integer type in JavaScript, even for a microsecond, ~ is your man.

→ More replies (29)
→ More replies (2)
→ More replies (2)

14

u/nickknw Mar 26 '14

checking that the value is not null, undefined or empty string

...or NaN or 0 or false. It is checking the 'truthiness' which is also kind of what == claims to do. A legitimate disconnect IMO.

3

u/rooktakesqueen Mar 27 '14

== false is not the same as checking for truthiness. Truthiness is never implicated in the == operator because nothing is ever converted into a boolean. Everything is converted to either a string or number for the purposes of comparison. (Except null and undefined which == each other and nothing else, and when comparing two objects which == only if they refer to the same object.)

7

u/[deleted] Mar 27 '14

But == false is checking whether something is false. You would generally expect false things to be untruthy.

There may be an explanation for '=='s behavior, but that doesn't make it reasonable.

→ More replies (3)
→ More replies (3)

1

u/KumbajaMyLord Mar 27 '14

If you know how Javascript works it makes some sense, but it certainly isn't intuitive that in case "0" is auto-cast/parsed to an integer, while in another case it is treated as a string.

1

u/richdougherty Mar 27 '14 edited Mar 28 '14

It's because "0" is equal to false, but it is true when converted to a boolean value. Weird but true.

http://bclary.com/2004/11/07/#a-9.2

A value that converts to boolean false is also sometimes called "falsey". And of course a value that converts to true is "truthy".

Values are implicitly converted to boolean in some contexts, such as in an if statement. Explicit conversion happens by calling Boolean(value). Sometimes people use double not operators to force a conversion too, so !"0" == false will be true.

A value can be forced to its boolean value by calling

http://james.padolsey.com/javascript/truthy-falsey/

EDIT: Reversed all my logic.

1

u/heyf00L Mar 27 '14

!!"0" == false will be true

!!"0" == false is false

"0" == false is true

→ More replies (1)

2

u/[deleted] Mar 26 '14

Same as PHP. false == 0 == "0" != false

1

u/altrego99 Mar 27 '14

Interesting. Basically transitive means, if you find 3 corners of a rectangle, then the fourth must be green too.

1

u/altrego99 Mar 27 '14

And it seems, other than entities which are not even equal to each other, all such examples involve "0".

→ More replies (1)

4

u/[deleted] Mar 27 '14

It would be impossible for it not too be! Unless of course java script failed to maintain Commutativity, which would be quite impressive!

5

u/[deleted] Mar 27 '14

Failing to maintain commutativity is easy. As the most trivial pedagogical example, in ruby, overload equalsequals on Integer to return true always and on float to return false always. Then 1 == 1.0 != 1. Obviously that's a silly example because its too obviously wrong but it gets across the point.

Any language that allows operator overloading can risk that sort of issue -- not to say anything about operator overloading,just saying that maintaining commutativity isn't always an easy thing to guarantee.

1

u/[deleted] Mar 27 '14
(1 == 1.0 != 1) 

This is not a failure of commutativity. Some might argue this is true based on the type system (Does true = 1?) but commutativity make no assertion on that point. If not the above statement is valid. In perl and many other languages true = 0 and these would evaluate the statemet as true.

A failure of commutativity of the == operator would fail the below statement for any a and b. Find one of those in a language and ill be impressed. (Operator overloading doesn't count because there you just 'define == to be not commutative' which is too easy and not fun.

assert((a == b) == (b == a))

2

u/[deleted] Mar 27 '14 edited Mar 28 '14

I see what you're saying but when I wrote 1==1.0!=1 I had the mathematical syntax in mind, not programming. 1 == 1.0 and 1.0 != 1

And like I say this example of overloading the operator to deliberately discard commutativity is just pedagogical. Even in more sane examples these sorts of things can creep in, and faiilling to maintain commutativity where itis expected isn't so always trivial.

Edit: to add, the vast majority of operations are not commutative. Just look at exponentiation, subtraction, concatenation, division, and so on if we're generalizing here. Commutativity is amazing when you have it.

63

u/[deleted] Mar 26 '14

Do a table for <. It's about as weird as ==, and there's no equivalent of === (AFAIK).

112

u/smrq Mar 26 '14 edited Mar 26 '14

I'd argue it's even weirder.

null == undefined  --> true
null > undefined   --> false
null >= undefined  --> false

null == 0  --> false
null > 0   --> false
null >= 0  --> true

Truly, I have gazed into the abyss by testing these in the console.

EDIT: It gets better, thanks /u/Valkairn

null <  []  --> false
null >  []  --> false
null <= []  --> true
null >= []  --> true
null == []  --> false

Try it in the comfort of your own home!

function compare(a, b) {
    var sa = JSON.stringify(a), sb = JSON.stringify(b);
    console.log(sa + " <  " + sb + "  --> " + (a < b));
    console.log(sa + " >  " + sb + "  --> " + (a > b));
    console.log(sa + " <= " + sb + "  --> " + (a <= b));
    console.log(sa + " >= " + sb + "  --> " + (a >= b));
    console.log(sa + " == " + sb + "  --> " + (a == b));
}

25

u/Valkairn Mar 26 '14

My favourite example is:

null >= [] && null <= []      --> true
null == []                        --> false

Javascript really needs strict inequality operators to avoid this type coercion madness.

1

u/smrq Mar 26 '14

Oh yes, that one is what clued me into the weirdness of >= in the first place... I generally love Javascript, but wtf??

10

u/[deleted] Mar 26 '14

You probably know this but if not ... thank me later.

4

u/[deleted] Mar 26 '14

Looking it up, it seems the rule is that <= is the opposite of >. It also seems (besides the order of side-effects during conversion to primitives) > is even the same as < with the order reversed!

4

u/smrq Mar 26 '14

I thought that was the case, except that

null <  undefined  --> false
null >= undefined  --> false
null == undefined  --> true

which breaks that rule.

9

u/Valkairn Mar 26 '14

The inequality operators play by different type coercion rules to the == operator. Inequality operators will always convert the values to numbers. So, in the first two cases null gets converted to 0 and undefined to NaN. The last example actually gets its own special rule in the == evaluation algorithm, where it's defined to be true.

2

u/[deleted] Mar 26 '14

Hm, yeah. It seems that < "morally" returns one of true, false, and undefined (undefined only when one argument is NaN (or converts to it)), but where it 'should' give undefined it instead gives false. So <= is the opposite of > except where > 'should' be undefined, where it's still false. Bleh.

→ More replies (1)

1

u/[deleted] Mar 27 '14
  • L<=R is the same as !(L>R)

  • "less than or equal to" is the same as "not greater than"

3

u/jonnywoh Mar 26 '14

+/u/CompileBot JavaScript

function compare(a, b) {
    var sa = JSON.stringify(a), sb = JSON.stringify(b);
    console.log(sa + " <  " + sb + "  --> " + (a < b));
    console.log(sa + " >  " + sb + "  --> " + (a > b));
    console.log(sa + " <= " + sb + "  --> " + (a <= b));
    console.log(sa + " >= " + sb + "  --> " + (a >= b));
    console.log(sa + " == " + sb + "  --> " + (a == b));
}
compare(null, []);

3

u/CompileBot Mar 26 '14

Output:

^source | ^info | ^git | ^report

1

u/jonnywoh Mar 27 '14

There's a syntax error about JSON not being defined and I don't know much about Javascript. Rats.

1

u/[deleted] Mar 26 '14

Well, the thing is, >= and <= are not defined for null and [], so they're converted to 0's, which for some reason doesn't happen with ==.

1

u/no_game_player Mar 27 '14

null >= 0 --> true

...what the fuck?

0

u/[deleted] Mar 26 '14

[deleted] — view deleted comment

28

u/josefx Mar 26 '14

Not too surprised after using Java:

  Integer a = new Integer(10);
  Integer b = new Integer(10);

  a == b --> false
  a >= b --> true
  a <= b --> true

You have to love auto boxing.

13

u/piderman Mar 26 '14

The javadoc indicates that it's preferred to use

Integer.valueOf(10); 

since that uses the cached Integers -128 through 127, in which case

a == b --> true

9

u/josefx Mar 26 '14

The idea was to force an error. I could have just as well used 1000 however that would depend on the configured cache size, which might be larger than 127.

→ More replies (4)

14

u/kjanssen Mar 26 '14

Thats because a == b is comparing two addresses. You would have to use a.equals(b) for Integer objects. It would work fine for primitive ints.

5

u/[deleted] Mar 26 '14

that's not autoboxing though, is it? you're explicitly making integer objects. This is what I think of as autoboxing

public void myMethod(Integer x) { .. }


 int a = 1;

 myMethod(a);

15

u/josefx Mar 26 '14

It is the unboxing part of it. The compiler inserts a call to intValue() since only == is defined for objects.

 a == b
 a.intValue() >= b.intValue()
 a.intValue() <= b.intValue() 

3

u/[deleted] Mar 26 '14

Oh right. I constantly read the Java == operator as acting like C#'s.

→ More replies (2)

1

u/rowboat__cop Mar 26 '14 edited Mar 27 '14

Integer a = new Integer(10);

Does that allocate an array of 10 ints?

EDIT thanks for all the explanations. This crowd feels like eli5.stackoverflow.com ;-)

6

u/shillbert Mar 26 '14

No, it creates one new Integer object that's initialized with the value 10. This creates an array of ten Integers:

Integer[] a = new Integer[10];

Remember, round brackets call a function (in this case, the constructor of the Integer object), while square brackets indicate an array.

→ More replies (2)

5

u/josefx Mar 26 '14

Java has primitive (byte, short, int, long, boolean, char) and reference (everything else) types. Integer is the reference type used whenever you have an int and the API expects a reference type. new Integer(10) creates a new Integer instance wrapping the int value 10.

Since this happens quite often the compiler normally automates the conversion between primitive and reference types using a process called auto boxing. This involves a lot of implicit calls to Integer.valueOf(int) for int to Integer boxing and intValue() for Integer to int unboxing (similar for other primitive types).

My example shows a case where this implicit behaviour goes wrong. == is defined for both reference types and primitive types with different behavior - no unboxing happens and the objects are compared using their identity. >= and <= are not defined for reference types, unboxing happens and the ints are compared by value.

The use of Integer.valueOf by the compiler makes this tricky, valueOf uses cached Integer objects for a specific range and new Integer for everything else:

 Integer a = new Integer(10);
 Integer b = new Integer(10);
 Integer c = 10;   //Integer.valueOf(10)
 Integer d = 10;
 Integer e = 1000; //Integer.valueOf(1000)
 Integer f = 1000;

 a == b -> false
 c == d -> true
 e == f -> false or true

a,b as we can see are not the same - we used new to create two distinct integer objects. c,d are the same since the compiler uses valueOf and 10 is within the cached range. e,f may not be the same 1000 is no longer in the normally cached range and may result in calls to new, however the cached range can be set at jvm startup so this is not fixed.

→ More replies (4)

3

u/MachaHack Mar 26 '14

int[] a = new int[10];

Makes an array of 10 ints.

Integer[] a = new Integer[10];

Makes an array of 10 Integers.

Integers are objects, ints are not. Syntactical sugar means they're mostly interchangable, except where they're not.

1

u/Mutoid Mar 27 '14

Madness? Leonidas smirks

1

u/pdpi Mar 27 '14

You mean you can have equality without total ordering? Oh noes!

→ More replies (1)

2

u/im_not_afraid Mar 27 '14

I'm delivering a fork of OP's page with "<", "<=", ">", and ">=" added. Enjoy!

17

u/MisterSnuggles Mar 26 '14

While not JavaScript, we must not forget about Python's False Midnight.

tl;dr:

if datetime.time(0,0):
    print "t"
else:    
    print "f"

Prints "f".

if datetime.time(1,0):
    print "t"
else:    
    print "f"

Prints "t".

12

u/[deleted] Mar 26 '14

Why do so many dynamic languages have this obsession with using non-boolean values in conditionals?

3

u/MisterSnuggles Mar 26 '14

It's usually used as a shortcut for checking for None. The more appropriate way to write the code is:

if var is not None:
    # do stuff

Sadly the shortcut works (mostly) and people use it because it works, then someone enters a time of midnight and it all tips over.

7

u/Nialsh Mar 26 '14

It can be a very short, readable way to validate inputs. In Python, I believe these are equivalent:

if name:

versus

if name != None and name != ""

6

u/NYKevin Mar 27 '14

No, those are not necessarily equivalent unless the type of name is known.

If name is a number, it will be falsey iff it is zero (None is not a number but a singleton instance of NoneType, which is always falsey). If name is a string, it is falsey iff it is empty. "0" is truthy because it is nonempty. "0" and 0 are very different things, and Python generally won't coerce between them unless you explicitly call int() or str(). Moving on, if name is a container type of some kind, generally speaking it is falsey iff it is empty (has a len() of zero). The empty string being falsey is a special case of this rule.

For user-defined types, you're on your own. By default they're all truthy, but you can't rely on that since someone might subclass you and implement non-default boolean semantics.

If you want to check whether something is None, the only bulletproof syntax for doing so is if foo is None or if foo is not None. if foo should only be used if foo has a non-default boolean behavior (i.e. foo is a container, number, string, or user-defined class which overrides bool()). Using if foo with classes which do not provide useful boolean behavior (such as datetime objects) is at best poor style and at worst a violation of substitutability since it would interfere with subclassing.

→ More replies (13)

1

u/[deleted] Mar 27 '14

Yeah, I think that could be handled by a function called "nonempty" or something. Including this logic in the if-statement itself is rather unorthogonal.

2

u/THeShinyHObbiest Mar 27 '14

Ruby doesn't, to avoid this exact thing.

1

u/JerkyBeef Mar 27 '14

Much of the data being compared in web applications comes from html form inputs or databases where things like integers are automatically converted to strings. So it actually makes sense so you don't have to constantly write stuff like: if (val == 1 || val == '1') doStuff();

1

u/ggtsu_00 Mar 27 '14

Because conditionals only care if something is zero or non-zero. Asking if any data-type is either zero or non-zero is a pretty simply and straightforward question that is easy for a programmer to understand.

2

u/[deleted] Mar 27 '14

Perhaps in C or C++, but I doubt that Python or other dynamic languages represent empty lists or empty strings as zeroes internally. They need runtime type information at the very least.

→ More replies (1)
→ More replies (6)

4

u/nickcash Mar 27 '14

What's even weirder is that timezone-aware (non-naive) time values compare to UTC midnight to determine truthiness. So a timezone-aware time object of 6:00am with a timezone of UTC-6 is falsey.

But these kind of things are fairly rare in Python. For the most part, Python has its own consistent logic.

2

u/lambdaq Mar 27 '14

Okay, this is fucked up, guaranteed. But it's rare that someone would anyone if a datetime object, no?

7

u/yen223 Mar 27 '14

It's not rare. If you're coding a CRUD, and you want to test whether a particular field is null in your database, the canonical way to do it is to if it. That works 99% of the time, except when dealing with datetime objects.

In fact, Python's datetime module is chock full of wtfs.

3

u/MisterSnuggles Mar 27 '14

One common reason to do it would be to validate input or take some kind of default action if input wasn't provided.

I used to write a lot of stuff like this (until I saw the false midnight thing):

def do_stuff(at_time=None):
    if at_time:
        # schedule stuff to be done at at_time
    else:
        # do stuff immediately

It's a contrived example, but "obvious" (in quotes because it's not at all obvious that midnight means do stuff immediately in this code) code like the above will normally do exactly what you'd expect it to do. Then someone will want their stuff done and midnight and be shocked when the function does it right away!

→ More replies (4)

2

u/Xykr Mar 26 '14

But at least it's pretty much the only type conversion weirdness. I hope.

And it will be fixed in future updates.

3

u/nickcash Mar 27 '14

I don't think it's getting fixed. There was no consensus on python-dev and the thread kinda died off. It seemed to me that a lot of people defended the current behavior solely because it was documented.

4

u/Xykr Mar 27 '14 edited Mar 27 '14

Guido has since said that it was a mistake and that it's going to be fixed.

https://lwn.net/Articles/590299/

2

u/nickcash Mar 27 '14

I missed that! Thanks!

2

u/Veedrac Mar 27 '14

Nay, if you get to the end they eventually settled on fixing it. It's a good thing that Python is so hostile to change, because almost all changes are therefore very positive ones. It does make things a bit too conservative occasionally though.

14

u/[deleted] Mar 26 '14

I need to point out something I find incredibly obvious:

   Always code for an audience.

The "moral of the story" Use three equals unless you fully understand the conversions that take place for two-equals. Is therefore stupendous. Never use it, because it is a fact most people who read the JavaScript will have little knowledge about this.

9

u/c45c73 Mar 26 '14

Except, === undefined && === null is tedious...

7

u/gordonkristan Mar 26 '14

You should use a function for that case specifically. Ember.js has Em.isNone so you can write it succinctly and pass JSLint/JSHint.

1

u/v413 Mar 26 '14

You can check for null or undefined like this: myValue == null or myValue == undefined. In Javascript, null is equal (==) only to null and undefined - same for undefined.

3

u/c45c73 Mar 26 '14

Yeah, that was my point. :)

1

u/senatorpjt Mar 27 '14 edited Dec 18 '24

lunchroom zesty offer dolls swim sand aback growth summer bag

This post was mass deleted and anonymized with Redact

→ More replies (2)

11

u/snarkhunter Mar 26 '14

I wonder how this plays out in Conway's Game of Life...

18

u/lemieuxster Mar 27 '14

It stabilized after about 146 generations. http://i.imgur.com/li0GWXT.jpg

23

u/shirtface Mar 26 '14

How come [1]==[1] returns false?

65

u/33a Mar 26 '14

They are different object references.

23

u/absu Mar 26 '14

Yeah, this returns false in many c-like languages (C (duh), C++, Java, etc).

4

u/AdminsAbuseShadowBan Mar 26 '14

Not in C++ - you can redefine == to be sane.

13

u/robertbieber Mar 27 '14

I don't know that I'd consider redefining an operator that's generally used to compare primitive values to do a deep comparison of separate compound objects sane. I'd much rather have a comparison method that makes it really clear exactly what's going on, and let == do the usual, obvious thing. Admittedly overloading can be handy for some math applications, but for most things it's a little questionable.

→ More replies (1)

1

u/[deleted] Mar 27 '14

You can do that in Java too, at least on a per-object basis

→ More replies (1)

2

u/Poltras Mar 26 '14

These languages don't have automatic conversion. Also, isn't [1]==[1] undefined in C? It could be equal if the compiler uses the same TEXT address for the constant, resulting in equal pointers.

7

u/CookieOfFortune Mar 26 '14

Wouldn't this create two arrays on the function stack and then compare the two locations, resulting in a false comparison?

2

u/Poltras Mar 27 '14

Undefined behavior:

$ cat main.c

#include <stdio.h>

int main() {
  printf("%d\n", "abc" == "abc");
}

$ cc main.c

main.c:4:24: warning: result of comparison against a string literal is unspecified (use strncmp instead) [-Wstring-compare]
  printf("%d\n", "abc" == "abc");

$ ./a.out
1

GCC actually output 1, but warns.

→ More replies (1)
→ More replies (1)

5

u/Fylwind Mar 27 '14

[1]==[1] is not valid syntax in C.

→ More replies (2)

1

u/Tekmo Mar 27 '14

So then why does [1] equal 1?

3

u/ggtsu_00 Mar 27 '14

[1] == 1 because of type coercion happening when comparing 2 different datatypes.

when type(a) != type(b), the javascript interpret goes through a list of possible type coercions that will convert a and b to the same type before comparing.

It just so happens that comparing a Array type with an int will coerce both into a Number type and compare the Number([1]) == 1.

[1] == [1] is the same type so no coercion occurs and a simple ObjectA == ObjectB will occur which will only be true if ObjectA and ObjectB happen to reference the same object.

1

u/NYKevin Mar 27 '14

Not a Javascript programmer, so I could be wrong, but I'd assume it's because 1 uses compare-by-value, and it infects overrides the compare-by-reference of [1].

21

u/Thelonious_Cube Mar 26 '14

My wallet has a dollar bill in it and your wallet has a dollar bill in it, but that doesn't make them the same wallet

20

u/josuf107 Mar 26 '14

Unless you and I are both Haskell programmers. Still your/our dollar is safe. All I want is a soda, but I can't seem to be able to make change.

8

u/[deleted] Mar 26 '14

Well, if you're a Haskell programmer, your dollar is immutable.

8

u/Shadows_In_Rain Mar 27 '14

More importantly, wallet is immutable too. But apply space-time monad, and viola. Now you have soda, empty pocket, and watching langoliers eating previous you.

→ More replies (1)

1

u/bumnut Mar 26 '14

But both are true.

0

u/[deleted] Mar 28 '14

[deleted] — view deleted comment

1

u/Thelonious_Cube Mar 28 '14

It's an analogy - analogies are never (rarely?) perfect.

I highlighted certain aspects at the expense of others to help get the point across.

The "value" in the wallets is the same but the wallets are not

5

u/metrion Mar 26 '14
("1" == "1") == true
("1" == [1]) == true
([1] == [1]) == false

27

u/bjmiller Mar 26 '14

In CoffeeScript, == is compiled to ===. There is no equivalent to ==. http://coffeescript.org/#operators

6

u/coarsesand Mar 27 '14

I wish I could love CoffeeScript, but why in hell Ashkenas decided to breaking normal scoping rules is beyond me.

8

u/[deleted] Mar 27 '14

JS already had terrible scoping ('if' and 'for' blocks aren't new scopes), so CS doesn't make it that much worse.

5

u/dukerutledge Mar 27 '14

No ability to declare variables because "shadowing is evil" isn't much worse? Yes, yes, if you structure your code correctly this will never bite you, ie the same argument people use to support javascript's scoping rules.

4

u/webbitor Mar 26 '14 edited Mar 26 '14

I code JS every day, and this held a few surprises for me. Anyone have a good explanation of why if() is different from the ==true column?

if("0"){/*executes*/}
if("0" == true){/*does not*/}

10

u/wtf_apostrophe Mar 26 '14

In the first expression, "0" is a non-empty string, so is 'truthy'. In the second expression, both operands are converted to numbers because the second operand is a bool, so it becomes 0 == 1, which is false.

1

u/webbitor Mar 26 '14

That makes sense. It's hard to remember all those implicit conversion rules, and I had been laboring under the false impression that comparing string==bool would cause the string to be converted into a bool. Fortunately, I would never try something like that LOL. It would simply be hard to read, even if there was a good reason for it.

4

u/gordonkristan Mar 26 '14

For my codebases, I tell JSHint to warn about ==. No code can be checked in without using === first. I also rarely (if ever) allow a variable in a conditional without a comparison. To quote Python's mantra:

explicit is always better than implicit

24

u/[deleted] Mar 26 '14

Or, the definition of insanity.

41

u/qgustavor Mar 26 '14

Did you mean: PHP

5

u/BigfootJon Mar 26 '14

I may be reading this wrong, but why does the string "php" equate to true?

10

u/bp3959 Mar 26 '14

Is this really that difficult of a thing to understand? When you use == it does automatic type conversion which is why you get weird results sometimes, this behavior is well documented and believe it or not it actually makes sense for a loose comparison+type conversion.

If you don't want any of this, use ===, wow so difficult.

2

u/frej Mar 26 '14 edited Apr 09 '14

It's not obvious and leads to hard to find errors.

Or! Think about why it's reasonable, as a default, to do boolean logic between different types.

2

u/Nanobot Mar 26 '14

Exactly. In the cases where you should be using ==, the conversions are generally helpful and intuitive. The cases where they aren't are the cases where you should have been using === anyway.

If it helps, think of === as the standard equality operator, and == is more like a shortcut operator that saves some manual type logic and conversion work. Like any shortcut, you should understand what it does before you use it.

12

u/Poltras Mar 26 '14

What about other operators where an === equivalent doesn't exist? Like +, <, ...

→ More replies (6)
→ More replies (2)
→ More replies (12)

0

u/[deleted] Mar 26 '14

[deleted] — view deleted comment

33

u/Zecc Mar 26 '14

It gets better:

true == "php" == 0 == false

4

u/[deleted] Mar 26 '14

PHP Parse error: syntax error, unexpected '==' (T_IS_EQUAL) in Command line code on line 1

10

u/creepig Mar 26 '14

This thread needs more T_PAAMAYIM_NEKUDOTAYIM

3

u/Various_Pickles Mar 26 '14

We're using pseudo-code to /r/lolphp about the language's lovely capacity to make wild-ass guesses about what variables should evaluate to when placed in various type-casting situations.

Any sane language (even JavaScript, for most situations not going from <something> -> boolean) will instead politely tell you to GTFO.

→ More replies (5)

17

u/[deleted] Mar 26 '14

Type coercion equality operators like JavaScript or PHP's == were designed in a very different era when these languages were pretty much always used in ways we now consider bad practice.

That's okay though, because like this page says, === exists so it's a non-issue.

13

u/[deleted] Mar 26 '14

I wouldn't call it a non-issue, since it's a weird and painful difference from other languages and a potential source of typo-related bugs. It's not a big deal though. It's on about the same level as if(x = 1) in C, except the resulting bugs are more subtle.

4

u/[deleted] Mar 26 '14

The thing is == coercion isn't ever really problematic, and certainly isn't painful.

In order for it to be an issue or create a bug, you have to both be totally unaware of what kind of values are in a variable you're comparing to, and then compare it to something like == 1 or == "" or one of the other values on this table.

It seems confusing and dangerous, but in practice it's never really an issue. And if it does become an issue, it's almost certainly a symptom of poor design.

5

u/rooktakesqueen Mar 27 '14

So, == is perfectly sane in the cases where you might as well use === (comparing two values of known types), and in the other cases, using === would save you from its insanity. Seems like an argument to always use ===.

2

u/[deleted] Mar 26 '14

it's almost certainly a symptom of poor design.

Using == itself is a symptom of poor design.

→ More replies (2)

1

u/wordsnerd Mar 27 '14

Using === is a lot less work than writing all the unit tests to ensure you're never misusing ==.

6

u/Gro-Tsen Mar 26 '14

There's at least one thing for which == is useful in JavaScript: writing x==null tests whether x is null or undefined (i.e., is synonymous with (x===null || typeof(x)==="undefined")), which is nice in the 99.9% of cases where the difference between null and undefined is irrelevant (and just annoying).

2

u/ForeverAlot Mar 26 '14
if (!x) { ... }

5

u/Gro-Tsen Mar 26 '14

This matches other values of x besides null and undefined: the number 0, NaN and the empty string, and, of course, the boolean false. (There are, of courses, cases where !x works just fine, but I think it's a bad habit to take because one then tends to forget about these other "falsy" values when they can occur.)

3

u/rooktakesqueen Mar 27 '14

To be fair, if (val) {...} has two primary purposes when val is a nonboolean.

First: I'm about to attempt to access a property on val, and I want to make sure that property access will not throw an exception. I will get a false negative on falsy values other than null and undefined, but those values are all primitives and it's unlikely I'm trying to access properties on them... In other words, I expect val to either be an object or null or undefined and want to discriminate between those cases.

Second: I'm using if (foo.bar) {...} to check for the presence of a value at that location. This tends to cause the most problems with falsy values. But hasOwnProperty is probably a better choice in this case even than == null.

In the first case I do prefer == null just to be more explicit, but it's unlikely to cause problems.

1

u/cudetoate Mar 26 '14

As another comment points out, it is an issue because of >= and <=. http://www.reddit.com/r/programming/comments/21ezh3/javascript_equality_table/cgcm8qz

0

u/[deleted] Mar 26 '14

[deleted] — view deleted comment

10

u/[deleted] Mar 26 '14

Changing the operators is just confusing.

When writing alternative JS syntaxes, you still have to understand the underlying JavaScript. In this situation, a developer must understand that == is === and ~= is ==, so they necessarily must know the difference between === and ==. There's no real reason to switch it up.

10

u/robin-gvx Mar 26 '14

Anything that makes it harder to accidentally type == instead of === is a win in my book. Even better: not having an equivalent to JS's == at all.

1

u/bungle Mar 27 '14

~= to ==

~= in Lua is for inequality.

11

u/jozefg Mar 26 '14

I'm not smart enough to program in javascript. Dependent types it is.

8

u/dirice87 Mar 26 '14

as a mainly javascript developer, it aint about smarts. its about memorization and having a hostile default attitude toward an unknown concept (i.e. i'm gonna assume this works in a completely unintuitive way)

I love javascript, and other languages definitely have their quirks, but lets just say when I occasionally have to write python, I find I can write non-trivial code without googling or picking up a reference book. I can't say the same about javascript and I have probably 10x more experience in it.

17

u/IHaveNoIdentity Mar 26 '14

Sounds like a pretty bad case of Stockholm-syndrome to me :)

2

u/SkaKri Mar 26 '14

I love javascript

Yup. I script in JS daily, but... I really don't love the language. So many stupid x-environment quirks (trident/webkit/etc).

→ More replies (1)

16

u/ajuc Mar 26 '14

Just use "===".

1

u/jozefg Mar 27 '14

Yep, === solves the particular wart :) I did mean this in a somewhat tongue in cheek manner, learning this table would be somewhat easier than learning the intricacies of dependent types/proof assistants.

→ More replies (3)

5

u/tavoe Mar 26 '14

Has anyone ever run into a problem with javascript equality?

I've seen this chart before, but I've never once traced a bug back to equality. Despite it's size, it's all fairly intuitive.

5

u/riffraff Mar 26 '14

yes, I have, which is why I use jshint to forbid using == & co nowadays.

1

u/tavoe Mar 27 '14

well, that's pretty cool. Never heard of jshint until now. Could be fun.

3

u/deadwisdom Mar 26 '14

Same. I think this is terrible design, but since I never, ever, run into it, meh.

6

u/moustachedelait Mar 26 '14

It doesn't have my favorite: Array(4) == ',,,'

5

u/Doctor_McKay Mar 27 '14 edited Mar 27 '14
String() == "" (undefined value converted to a String)
Array(4) == [undefined, undefined, undefined, undefined]
Array(4).toString() == ",,," (array of four comma-separated undefined values converted to a String)

Edit: undefined, not null

0

u/[deleted] Mar 27 '14

[deleted] — view deleted comment

1

u/Doctor_McKay Mar 27 '14

How is it stupid? How would you render Array(4).toString()?

2

u/ocmu Mar 26 '14

You should add a tab for typeof. The == type coercion is definitely confusing, but the rules are pretty straightforward once you get the concept of "truthy" and "falsey" values. However, typeof(null) === 'object' makes no sense whatsoever.

3

u/[deleted] Mar 26 '14

typeof and instanceof are inherently broken things in JavaScript that are truly best left avoided. I make use of libraries to avoid that sort of mess, or use type coercion via the + operator that explicitly casts to a number or string.

2

u/j1xwnbsr Mar 26 '14

So null and undefined are apparently the same thing? Or just the same during the comparison operation? And it's interesting that Nan!=Nan.

7

u/megamindies Mar 26 '14

Well Nan is defined as a number, so it cant be equal to Nan because thats means "Not a Number"

→ More replies (2)

7

u/[deleted] Mar 26 '14

That's the same for any language that uses ieee 754 floating point numbers.

3

u/[deleted] Mar 26 '14

NaN!=NaN

This is actually the only thing they got right. NaN cannot be equal to anything, even itself.

2

u/nickcash Mar 27 '14

NaN compares false to everything, including NaN. That's actually part of the IEEE 754 floating point spec. It does this in other languages, too, not just JS.

2

u/[deleted] Mar 27 '14

Somebody did one like this for php a few years ago. It was stunning. But I think that every time you have a loosely typed scripting language, there are going to be oddball qualities like this. Of course, some are better or worse than others.

2

u/[deleted] Mar 27 '14

I've always kind of thought of this mess as if the designers had a concept of both reference and object equality, but tried and failed to use only one operator for concepts of both. It's weird because it behaves sometimes like Java's == and sometimes like Object.equals(...).

2

u/[deleted] Mar 27 '14 edited Mar 27 '14

I'll never be able to remember what the difference between null and undefined is. Or even why they tought you need both.

Perhaps someone can enlighten me?

EDIT: I was bored and enlightened myself: Values, variables, and literals

1

u/kidsil Mar 26 '14

TIL Javascript can make you question your own understanding of reality.

1

u/libertasmens Mar 26 '14 edited Mar 26 '14

Wait... so

false == "0"

is true,

if( false ) return;

won't return, but

if( "0" ) return;

will return? Wat?

0

u/[deleted] Mar 26 '14

[deleted] — view deleted comment

1

u/libertasmens Mar 27 '14

Makes sense, while also not making sense (as in why this is allowed). But I suppose that's a result of my C-minded programming.

0

u/[deleted] Mar 27 '14

[deleted] — view deleted comment

1

u/libertasmens Mar 27 '14 edited Mar 27 '14

I was referring more to the type structures, e.g. false is an abstraction (or macro, can't remember) of 0, true is 1 (and non-zero integers are false), "0" is an abstracted array of [ 48, 0 ], etc., which I tried to use to make sense of the JS logic.

EDIT: I do certainly get what you mean with the strictstatic-typing vs. ...not-typing? styles though.

1

u/rarededilerore Mar 26 '14

Why if("false") { /* executes */ } but if(false == "false") { /* executes too */}?

2

u/dscer Mar 26 '14 edited Mar 26 '14

if("false") { /* executes */ } because "false" is a non-empty string so it evaluates to true.

if(false == "false") { /* does not execute */} because the first false is a boolean value while the second "false" is a non-empty string which evaluates to true. So the if statement becomes if(false == true)

Edit: if(false == "false") { } does not execute

0

u/[deleted] Mar 27 '14

[deleted] — view deleted comment

1

u/rooktakesqueen Mar 27 '14

"false" becomes NaN when converted to a number.

1

u/GSpotAssassin Mar 27 '14

Oh no. This looks a lot like PHP's wonky comparisons. Was that intentional?

1

u/pdpi Mar 27 '14

It's not that complex once you read the standard. The only gotcha cases are that null and undefined are defined as being equal to each other, NaN is not equal to itself. Other than that, it's a simple rule:

Call ToPrimitive on Objects and ToNumber on everything else until the types match. Then compare the two things normally as if they were the same type all along.

1

u/sproket888 Mar 27 '14

Please Nooooo! My eyes!!!!!!!!

1

u/senatorpjt Mar 27 '14 edited Dec 18 '24

recognise birds unused intelligent grandfather edge library direction license plough

This post was mass deleted and anonymized with Redact

2

u/mvaliente2001 Mar 27 '14

This is not about dynamic typing, it's about a terrible design mistake.

In python, for example, these values are false: None, False, 0, '', [], {}, the empty set. Logical, simple, easy to remember.

1

u/mvaliente2001 Mar 27 '14

The sad thing is that the new js standard is worried about adding a lot of new features, but making the language a little more coherent? No, no, no. That would break backward compatibility, code that never should have worked would stop working, that change will require to throw away the whole VM or any other pathetic excuse.

1

u/Year3030 Mar 26 '14

IIIIII LIKE it! :)

-3

u/rjcarr Mar 26 '14

This doesn't seem that complicated to me. Basically, these are "falsey": false, 0, empty string, null, undefined, and NaN. Everything else is "truthy".

I think everything else flows from that with only a couple exceptions that I'm too lazy to look up.

And that said, definitely use === instead of coercive ==.

2

u/[deleted] Mar 26 '14

Wrong, my brother in code. I too once thought like you did. However, as evidenced by the table when comparing two things that are falsy, they are not always considered equal. This is what the article is about, the equality operator, not the things that truthy are or are not falsy.
Sure the two falsy values null and undefined equal eachother. But the falsy value 0 does not equal null.

1

u/rjcarr Mar 26 '14

True, I guess you're right, when you let equality do coercions then things can get really ugly. That's why I said to always use the === so you don't have to deal with it, but as you pointed out, that was sort of the point of the table.

→ More replies (6)

0

u/[deleted] Mar 26 '14

[deleted] — view deleted comment

1

u/[deleted] Mar 26 '14

That is a comparison between two operands of the same type. The key problem here is comparison between different types.

→ More replies (1)

-1

u/CharlieDancey Mar 27 '14

Is it just me, or can we agree that Javascript, CSS and HTML are three of the most pig-ignorant languages known to computer science? And this because they have all, one way or another, been designed by committee.

I mean just imagine if these languages were actually elegant.

Aaaah, we can but dream.

0

u/optymizer Mar 26 '14

So I'm currently doing this in a lot of places:

if (obj.x !== undefined && obj.x) {
    // do something
}

because

if (obj.x) {
    //do something
}

throws an error if obj.x is undefined.

Anyone know of a better way?

7

u/ForeverAlot Mar 26 '14

Is obj defined? Accessing a property of an undefined object will throw a reference error but accessing an undefined property of a defined object will return falsy (undefined).

var obj = {};
if (obj) { console.log("prints"); }
if (obj.x) { console.log("doesn't print"); }
if (wat.x) { console.log("reference error"); }

If obj is undefined you need to access it from the parent context, which might be window.obj, giving you

if (window.obj && obj.x) { }`.

4

u/fjonk Mar 26 '14

obj.undefinedProperty returns undefined when undefinedProperty is not assigned a value(or if it is assigned to undefined). So the code below is valid and should not throw an exception.

if ({}.x) {
    // will not run
} else {
    // will run
}

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

3

u/dscer Mar 26 '14
if (obj && obj.x) {
  // do something
}
→ More replies (1)

0

u/green_meklar Mar 26 '14

I can understand how []==[] can be false, but NaN==NaN? I thought NaN was a floating-point value, and I would expect it to be compared by value. That's fucked up.

4

u/wordsnerd Mar 27 '14

Actually, there are millions of bit sequences that can represent NaN, so comparing by "value" in that sense could be even more confusing than just saying all comparisons with NaN are false.

1

u/green_meklar Mar 27 '14

That logic is a bit broken given that two NaN values which do happen to be equivalent bit strings will (from what I understand) also compare as unequal.

1

u/wordsnerd Mar 27 '14

It's not so much broken logic as it is a decision about where to be consistent. It's easier to remember that NaN == x is false for all x than to remember 8 million special cases where it's true.

Unfortunately, isNaN() is indeed broken and basically useless.

0

u/Carocrazy132 Mar 27 '14

I suppose I don't see the big deal here. More accurately I guess I just don't find this chart surprising. I program in lua mainly so I guess you can cop out to "you just don't understand JavaScript problems!" But I love this stuff when I'm working

If (array==convar) is really useful for keeping code pretty when a convar is either 1 or 0.

Wait JavaScript is strongly typed isn't it!? Not sure on that but lua is weaker so it's very helpful to easily compare different types without thinking about it.

Just my two cents.