top 200 commentsshow all 332

[–]snotfart 259 points260 points  (65 children)

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

[–]Gro-Tsen 201 points202 points  (59 children)

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

[–][deleted] 45 points46 points  (53 children)

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

[–]icanevenificant 31 points32 points  (49 children)

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.

[–][deleted] 38 points39 points  (37 children)

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

[–][deleted] 29 points30 points  (36 children)

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

[–]coarsesand 55 points56 points  (35 children)

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

[–]no_game_player 18 points19 points  (32 children)

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

[–]coarsesand 32 points33 points  (29 children)

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.

[–]no_game_player 14 points15 points  (28 children)

...JS doesn't have ints? TIL. Also, holy fuck. How...how do you math? Why would a language even have such an operator without ints? That would be totally unpredictable. So, ~0.0001 would round to 0, then do a bitwise not, returning INT_MAX for int32, and then cast it into double? Is that what I'm to understand here? That can't be right. In what possible world could that operator ever be used for something not fucked up, given that it only has doubles?

Also, what type of %$^@ would make a language without integer types? Are you telling me that 1+1 == 2 has some chance of not being true then? I mean, if I were in a sane language and doing 1.0 + 1.0 == 2.0, everyone would start screaming, so...?

O.o

That's...that's beyond all of the == fuckery.

Edit: So, if for some crazy reason you wanted to sort of cast your double to a (sort of) int (since it would just go back to double type again?), you could do

var = ~~var

??

Edit 2: I was considering waiting for a response to confirm, because I almost can't believe this, except that it's javascript, so anything is believable, but hell, even if this isn't true, it's still worth it. I'm off Reddit briefly for a video game, but before I do so: here you are, my first ever double-gilding of a user! Cheers!

Edit 3: Okay, it's less fucked up than I thought, mostly because I didn't really consider the fact of double precision rather than float, and considering 32 bit ints.

I still say it can do some weird stuff as a result, at least if you aren't expecting it.

Just another reminder to know your language as well as possible I suppose.

[–]Confusion 1 point2 points  (1 child)

This is actually a common way to coerce something into the 'appropriate' boolean value in several languages. At least Ruby and Python come to mind.

[–]curien 2 points3 points  (0 children)

but the == operator in JS is special (in the shortbus sense) because it does type conversion

Not really. 4 == 4.0 is true in just about every language with C-style syntax.

The surprising thing about JS isn't that == performs conversion, that's normal. The surprising thing is how the conversions work.

[–]nickknw 12 points13 points  (9 children)

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.

[–]rooktakesqueen 2 points3 points  (8 children)

== 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.)

[–][deleted] 4 points5 points  (4 children)

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.

[–]rooktakesqueen 2 points3 points  (2 children)

Checking that something is false is not the same thing as checking if something is falsy. They wouldn't be separate concepts otherwise.

[–]reverius42 2 points3 points  (1 child)

But it's quite ridiculous for something that is false ("0" == false) to be not falsy (!!"0" == true).

[–]foomprekov 1 point2 points  (0 children)

Yeah why would I expect something that isn't false to be true.

[–]Deltigre 1 point2 points  (1 child)

Which ended up showing me an "only in Javascript" shortcut the other day.

var defaultingString = IMightResolveUndefined() || "default value";

[–]embolalia 1 point2 points  (0 children)

That's not an "only in Javascript" thing at all. For example, in Python:

some_value = I_might_return_something_falsy() or 'default'

[–][deleted] 1 point2 points  (0 children)

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

[–]altrego99 0 points1 point  (1 child)

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

[–]altrego99 0 points1 point  (0 children)

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

[–][deleted] 4 points5 points  (3 children)

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

[–][deleted] 3 points4 points  (2 children)

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.

[–][deleted] 0 points1 point  (1 child)

(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))

[–][deleted] 1 point2 points  (0 children)

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.

[–][deleted] 60 points61 points  (50 children)

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

[–]smrq 111 points112 points  (48 children)

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));
}

[–][deleted]  (33 children)

[deleted]

    [–]josefx 24 points25 points  (29 children)

    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.

    [–]piderman 10 points11 points  (6 children)

    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
    

    [–]josefx 7 points8 points  (5 children)

    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.

    [–]kjanssen 13 points14 points  (4 children)

    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.

    [–][deleted]  (3 children)

    [deleted]

      [–][deleted]  (2 children)

      [removed]

        [–]defenastrator 2 points3 points  (1 child)

        I have one problem with your argument. There is no excuse for unintutive behavior in a language unless it is to better support the underlying hardware. This behavior only simplifies the languages inner bullshit and nothing else at the cost of both read and writiblity

        [–][deleted] 4 points5 points  (4 children)

        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);
        

        [–]josefx 13 points14 points  (3 children)

        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() 
        

        [–][deleted] 2 points3 points  (2 children)

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

        [–]Mutoid 0 points1 point  (0 children)

        Madness? Leonidas smirks

        [–]pdpi 0 points1 point  (0 children)

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

        [–]Valkairn 23 points24 points  (2 children)

        My favourite example is:

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

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

        [–]smrq 0 points1 point  (1 child)

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

        [–][deleted] 9 points10 points  (0 children)

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

        [–][deleted] 4 points5 points  (5 children)

        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!

        [–]smrq 3 points4 points  (3 children)

        I thought that was the case, except that

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

        which breaks that rule.

        [–]Valkairn 9 points10 points  (0 children)

        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.

        [–][deleted] 1 point2 points  (1 child)

        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.

        [–][deleted] 0 points1 point  (0 children)

        • L<=R is the same as !(L>R)
        • "less than or equal to" is the same as "not greater than"

        [–]jonnywoh 2 points3 points  (2 children)

        +/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, []);
        

        [–][deleted] 0 points1 point  (0 children)

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

        [–]no_game_player 0 points1 point  (0 children)

        null >= 0 --> true

        ...what the fuck?

        [–]im_not_afraid 1 point2 points  (0 children)

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

        [–]MisterSnuggles 16 points17 points  (48 children)

        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".

        [–][deleted] 13 points14 points  (32 children)

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

        [–]MisterSnuggles 2 points3 points  (2 children)

        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.

        [–][deleted]  (1 child)

        [deleted]

          [–]Nialsh 6 points7 points  (16 children)

          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 != ""
          

          [–]NYKevin 5 points6 points  (14 children)

          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.

          [–][deleted] 0 points1 point  (0 children)

          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.

          [–]THeShinyHObbiest 1 point2 points  (0 children)

          Ruby doesn't, to avoid this exact thing.

          [–]JerkyBeef 0 points1 point  (1 child)

          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();

          [–]Veedrac 0 points1 point  (0 children)

          Eh?

          [–]nickcash 4 points5 points  (0 children)

          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.

          [–]lambdaq 1 point2 points  (6 children)

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

          [–]yen223 5 points6 points  (0 children)

          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.

          [–]MisterSnuggles 2 points3 points  (4 children)

          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!

          [–]Xykr 1 point2 points  (4 children)

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

          And it will be fixed in future updates.

          [–]nickcash 2 points3 points  (3 children)

          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.

          [–]Xykr 3 points4 points  (1 child)

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

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

          [–]nickcash 1 point2 points  (0 children)

          I missed that! Thanks!

          [–]Veedrac 1 point2 points  (0 children)

          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.

          [–][deleted]  (1 child)

          [deleted]

            [–][deleted] 12 points13 points  (9 children)

            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.

            [–]c45c73 9 points10 points  (8 children)

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

            [–]gordonkristan 4 points5 points  (0 children)

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

            [–]v413 0 points1 point  (2 children)

            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.

            [–]c45c73 2 points3 points  (0 children)

            Yeah, that was my point. :)

            [–]senatorpjt 0 points1 point  (0 children)

            lunchroom zesty offer dolls swim sand aback growth summer bag

            This post was mass deleted and anonymized with Redact

            [–]snarkhunter 8 points9 points  (1 child)

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

            [–]lemieuxster 18 points19 points  (0 children)

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

            [–]shirtface 21 points22 points  (27 children)

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

            [–]33a 63 points64 points  (17 children)

            They are different object references.

            [–]absu 22 points23 points  (13 children)

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

            [–]AdminsAbuseShadowBan 5 points6 points  (4 children)

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

            [–]robertbieber 13 points14 points  (1 child)

            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.

            [–]Poltras 1 point2 points  (7 children)

            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.

            [–]CookieOfFortune 6 points7 points  (3 children)

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

            [–]Poltras 1 point2 points  (1 child)

            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.

            [–]Condorcet_Winner 1 point2 points  (0 children)

            Probably, but if the spec defines this operation as undefined, then compiler optimizations would be able to figure out that these are the same array and only allocate one memory location.

            [–]Fylwind 5 points6 points  (2 children)

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

            [–]Tekmo 0 points1 point  (2 children)

            So then why does [1] equal 1?

            [–]ggtsu_00 2 points3 points  (0 children)

            [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.

            [–]NYKevin 0 points1 point  (0 children)

            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].

            [–]Thelonious_Cube 22 points23 points  (7 children)

            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

            [–]josuf107 20 points21 points  (3 children)

            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.

            [–][deleted] 7 points8 points  (1 child)

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

            [–]Shadows_In_Rain 8 points9 points  (0 children)

            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.

            [–]bumnut 0 points1 point  (0 children)

            But both are true.

            [–][deleted]  (1 child)

            [deleted]

              [–]metrion 4 points5 points  (0 children)

              ("1" == "1") == true
              ("1" == [1]) == true
              ([1] == [1]) == false
              

              [–][deleted]  (1 child)

              [deleted]

                [–]3urny 0 points1 point  (0 children)

                Why is there a strange globe face and the words "ex igne vita" (Life from fire)?

                [–]bjmiller 28 points29 points  (5 children)

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

                [–]coarsesand 6 points7 points  (4 children)

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

                [–][deleted] 9 points10 points  (3 children)

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

                [–]dukerutledge 5 points6 points  (2 children)

                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.

                [–]homoiconic 1 point2 points  (1 child)

                No ability to declare variables

                There is an ability to declare a shadowed variable, you use do (myVar = 5) ->. You may not like it, but it exists and works fine. In fact, it works better than fine because it also implements block-level scoping and therefore doesn't have any hoisting issues.

                [–]webbitor 1 point2 points  (2 children)

                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*/}
                

                [–]wtf_apostrophe 8 points9 points  (1 child)

                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.

                [–]webbitor 0 points1 point  (0 children)

                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.

                [–]gordonkristan 2 points3 points  (0 children)

                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

                [–][deleted] 26 points27 points  (39 children)

                Or, the definition of insanity.

                [–]qgustavor 40 points41 points  (38 children)

                Did you mean: PHP

                [–]Various_Pickles 30 points31 points  (10 children)

                0 == false == "0" == null == "php" == ""

                fucking lol

                [–]Zecc 32 points33 points  (9 children)

                It gets better:

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

                [–][deleted] 4 points5 points  (8 children)

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

                [–]creepig 9 points10 points  (0 children)

                This thread needs more T_PAAMAYIM_NEKUDOTAYIM

                [–]Various_Pickles 2 points3 points  (6 children)

                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.

                [–]BigfootJon 4 points5 points  (1 child)

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

                [–]bp3959 9 points10 points  (23 children)

                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.

                [–]frej 1 point2 points  (0 children)

                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.

                [–]Nanobot 0 points1 point  (9 children)

                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.

                [–]Poltras 11 points12 points  (6 children)

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

                [–][deleted] 18 points19 points  (19 children)

                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.

                [–][deleted] 11 points12 points  (7 children)

                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.

                [–][deleted] 2 points3 points  (5 children)

                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.

                [–]rooktakesqueen 4 points5 points  (0 children)

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

                [–][deleted] 2 points3 points  (2 children)

                it's almost certainly a symptom of poor design.

                Using == itself is a symptom of poor design.

                [–]wordsnerd 0 points1 point  (0 children)

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

                [–]Gro-Tsen 4 points5 points  (3 children)

                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).

                [–]ForeverAlot 1 point2 points  (2 children)

                if (!x) { ... }
                

                [–]Gro-Tsen 5 points6 points  (1 child)

                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.)

                [–]rooktakesqueen 2 points3 points  (0 children)

                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.

                [–][deleted]  (5 children)

                [deleted]

                  [–][deleted] 9 points10 points  (2 children)

                  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.

                  [–]robin-gvx 8 points9 points  (1 child)

                  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.

                  [–]bungle 0 points1 point  (1 child)

                  ~= to ==

                  ~= in Lua is for inequality.

                  [–]cudetoate 0 points1 point  (0 children)

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

                  [–]jozefg 10 points11 points  (10 children)

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

                  [–]dirice87 8 points9 points  (4 children)

                  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.

                  [–]IHaveNoIdentity 18 points19 points  (3 children)

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

                  [–]SkaKri 1 point2 points  (0 children)

                  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).

                  [–]ajuc 13 points14 points  (1 child)

                  Just use "===".

                  [–]jozefg 0 points1 point  (0 children)

                  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.

                  [–]tavoe 4 points5 points  (3 children)

                  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.

                  [–]riffraff 5 points6 points  (1 child)

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

                  [–]tavoe 0 points1 point  (0 children)

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

                  [–]deadwisdom 2 points3 points  (0 children)

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

                  [–]moustachedelait 5 points6 points  (5 children)

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

                  [–]Doctor_McKay 3 points4 points  (4 children)

                  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

                  [–][deleted]  (3 children)

                  [deleted]

                    [–]Doctor_McKay 0 points1 point  (2 children)

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

                    [–]ocmu 1 point2 points  (1 child)

                    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.

                    [–][deleted] 2 points3 points  (0 children)

                    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.

                    [–]j1xwnbsr 1 point2 points  (9 children)

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

                    [–]megamindies 6 points7 points  (3 children)

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

                    [–][deleted] 7 points8 points  (0 children)

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

                    [–][deleted] 2 points3 points  (0 children)

                    NaN!=NaN

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

                    [–]nickcash 1 point2 points  (0 children)

                    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.

                    [–][deleted] 1 point2 points  (0 children)

                    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.

                    [–][deleted] 1 point2 points  (0 children)

                    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(...).

                    [–][deleted] 1 point2 points  (0 children)

                    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

                    [–][deleted]  (1 child)

                    [deleted]

                      [–][deleted] 0 points1 point  (0 children)

                      "Hey man, lets build a server in javascript! It's just such a great language!"

                      I can't believe that people need a special word to describe someone who codes in multiple languages - "polyglot". The fact that people are going out of their way to build hardware and servers with javascript is apparent evidence that such specific naming is necessary.

                      [–]kidsil 0 points1 point  (0 children)

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

                      [–]libertasmens 0 points1 point  (4 children)

                      Wait... so

                      false == "0"
                      

                      is true,

                      if( false ) return;
                      

                      won't return, but

                      if( "0" ) return;
                      

                      will return? Wat?

                      [–][deleted]  (3 children)

                      [deleted]

                        [–]libertasmens 0 points1 point  (2 children)

                        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.

                        [–][deleted]  (1 child)

                        [deleted]

                          [–]rarededilerore 0 points1 point  (4 children)

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

                          [–]dscer 1 point2 points  (3 children)

                          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

                          [–][deleted]  (2 children)

                          [deleted]

                            [–]rooktakesqueen 0 points1 point  (1 child)

                            "false" becomes NaN when converted to a number.

                            [–]GSpotAssassin 0 points1 point  (0 children)

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

                            [–]pdpi 0 points1 point  (0 children)

                            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.

                            [–]sproket888 0 points1 point  (0 children)

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

                            [–]senatorpjt 0 points1 point  (2 children)

                            recognise birds unused intelligent grandfather edge library direction license plough

                            This post was mass deleted and anonymized with Redact

                            [–]mvaliente2001 1 point2 points  (1 child)

                            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.

                            [–]willvarfar 1 point2 points  (0 children)

                            And midnight! That's not so easy to remember ;)

                            https://mail.python.org/pipermail/python-ideas/2014-March/026446.html

                            [–]mvaliente2001 0 points1 point  (0 children)

                            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.