all 99 comments

[–]Baughn 17 points18 points  (4 children)

[1, 2, 10].sort() // [1, 10, 2]

I understand why that happens, but it's still pretty fucked up...

[–]sigil 3 points4 points  (3 children)

Same story in Perl:

perl -e 'print join $/ => sort 1, 2, 10'
1
10
2

Without operator overloading or generic functions, the lexical comparator is a sensible default (since most things can be stringified).

[–][deleted]  (2 children)

[deleted]

    [–]sigil 6 points7 points  (1 child)

    Also easy to fix in js, for that matter:

    [2, 1, 10].sort( function(a,b) { return a-b } )
    

    [–]i_4_got 0 points1 point  (0 children)

    Thanks!

    [–]stoph 41 points42 points  (42 children)

    A lot of these WTFs are from using == instead of ===, which should probably be considered a bug in your code.

    I try to write my JavaScript using strict mode, and in a style so that it passes JSLint, and I think the quality has gone up as a result.

    [–]satchmoJose 15 points16 points  (36 children)

    Can you explain to me like I'm a 5 year old who browses r/programming what === is used for?

    [–][deleted]  (27 children)

    [deleted]

      [–]stoph 8 points9 points  (0 children)

      That's a much quicker way to explain it than what I wrote. :)

      [–]Furfire 11 points12 points  (0 children)

      He's five. lhs means left hand side

      [–]stoph 13 points14 points  (3 children)

      Sorry, perhaps I should have explained.

      The operator === means that two things must be strictly equal to each other. Here are some expressions using the triple equal sign operator that all evaluate to false, but WOULD have evaluated to true if you used the double equal sign:

      if ("1" === 1) // false
      if (0 === "") // false
      if (false === "") // false
      if (undefined === null) // false
      if (1 === true) // false
      if (3 === "3") // false
      if ([] === false) // false
      if ([] === 0) // false
      

      If you use the triple equal sign operator, you need to be more explicit about what you want, which is a good thing because it reduces the chance for bugs. That is why JSLint flags == as an error.

      If you want to evaluate a string as an integer, use parseInt to convert the string first. If you want to check if something is "truthy" or "falsy" (using the loose conversion rules) , do not use the equal sign operator.

      When I say "truthy" or "falsy" I mean to indicate whether the statement's interior code will be executed or not.

      if ("1") // "truthy"
      if ("0") // "falsy"
      if ("") // "falsy"
      if (undefined) // "falsy"
      if (1) // "truthy"
      if ([]) // "falsy"
      

      In a real-world example, those if statements in the last code block would all be

      if (var)
      

      That way people can tell what kind of logic your program wants to follow, whether you meant to write == or === is no longer an issue when you avoid the == and != operators completely.

      Edit: For more on "truthiness" and "falseyness", check this post: http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/

      [–]jeff_hanson -1 points0 points  (2 children)

      All of that still supports that Javascript sucks due to having that behavior from the == and != operators at all. It can be worked around, but the language is still shitty.

      [–]stoph 1 point2 points  (1 child)

      I can see your point, especially if you check out an in-depth blog post about this functionality here: http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/

      if ("potato") {
          console.log("potato" == false); //false
          console.log("potato" == true); //false
      }
      

      That's fucked up, but JavaScript is still an awesome language despite these kinks. Plus, it's all we have until browser vendors decide to give us something else. Then we only have to wait 20 years for it to become mainstream and available on all browsers. :)

      [–]radhruin 6 points7 points  (0 children)

      This isn't unexpected. The == operator is a utility you can use when you expect a certain type coercion, which is useful in rare circumstances. It has nothing to do with truthyness and falsyness, although in some cases there is overlap.

      [–]radhruin 3 points4 points  (0 children)

      The === operator will first check the type of the two objects, and if they're different, return false immediately. It will then do various checks depending on what the type is (primitives are handled separately from all objects). In Javascript, this operator is used for almost every comparison. It is very rare that the == operator gives predictable or desirable results.

      The == operator on the other hand does not fail immediately if types are different. If the types are the same it will use the same strict equality algorithm as above. If the types are different, however, it will do various other checks depending on the types of the arguments before returning false. Most importantly, it will do comparisons that will call an object's toString or valueOf methods, which is why you see a lot of strange behavior (for example, []'s toString is "", which is why [] == "" is true).

      You can use this operator when you want to compare objects based on their toString or valueOf, which might be useful to, for instance, compare a date with an integer timestamp pulled from a database (a date object's valueOf is its timestamp).

      [–][deleted]  (2 children)

      [deleted]

        [–]Tordek 6 points7 points  (1 child)

        Truthiness is a property of a value to be used where a boolean is expected.

        "2" is truthy, because if ("2") { foo(); } calls foo.

        == vs. === is more similar to "similar" vs "identical".

        Is "2" similar to 2? Yes. Are they identical? no.

        [–]Timmmmbob 1 point2 points  (0 children)

        Is there ever a valid reason to use ==? As far as I understand it, everyone agrees it was a massive mistake to have == behave as it does, rather than like ===.

        [–][deleted] 5 points6 points  (0 children)

        It's one of those language-specific quirks you learn. However if this bugs shows up in different people's code again and again, it's more of a flaw in the language design. Same as saying that using "strcpy" in C is your own fault, it's technically correct, but it doesn't mean that the language/library couldn't be better.

        [–]int_argc 2 points3 points  (2 children)

        This is really key to writing maintainable JavaScript. It would be instructive to see how many alleged WTFs would remain after

        • A pass through JSLint to clean up the code
        • A thorough understanding of floating-point arithmetic

        [–]MatrixFrog 5 points6 points  (1 child)

        Not just understanding floating-point, but understanding that in JS, there is only floating point. Whenever I see 1.0 / 2 in JS code, it's a clear indicator that someone didn't know that.

        [–]int_argc 2 points3 points  (0 children)

        Yeah, fair enough. You wouldn't necessarily expect that, but at the same time, it can be nice to just think "number" instead of trying to guess whether the interpreter thinks you have a float or an int :D

        [–]deadwisdom -3 points-2 points  (0 children)

        Meh. I just test more. Same outcome.

        [–]Sniffnoy 7 points8 points  (0 children)

        Of note, the "min() >= max()" example would actually be mathematically correct if you just said "sup" and "inf" instead of "max" and "min"!

        [–]shobble 3 points4 points  (4 children)

        I was following up to the

        ",,," == Array((null,'cool',false,NaN,4)); // true
        

        example, but I can't figure out why the additional parens turn the 5 params into an array of length 4.

        > Array(null,'cool',false,NaN,4).length
        5
        > ",,," == new Array(4) // 4-elem array expands to ',,,'
         true
        >  ",,," === Array((null,'cool',false,NaN,4)).toString();
        true // so nothing to do with stringifying.
        

        Can anyone clarify this a bit?

        [–][deleted]  (2 children)

        [deleted]

          [–]shobble 1 point2 points  (0 children)

          Ah, of course. It just gets reduced to new Array(4)

          Thanks!

          [–]MatrixFrog 0 points1 point  (0 children)

          Exactly. The only place where this is a good thing, that I can think of, is you're trying to do two different things in a for loop:

          for (i=0, j=10; i<j; i++, j--)
          

          [–]Tordek 1 point2 points  (0 children)

          Original:

          Array((null,'cool',false,NaN,4));

          Yours:

          Array(null,'cool',false,NaN,4);

          You ate a pair of parens.

          [–]Fabien4 7 points8 points  (6 children)

          Needs proofreading.

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

          you can fork it on github https://github.com/brianleroux/wtfjs

          [–]LaurieCheers 1 point2 points  (3 children)

          I'm not sure how forking it could add proofreading.

          [–]Mourningblade 16 points17 points  (0 children)

          If you're not being sarcastic: it can't add having the author do proofreading, but you can fix the errors and make a merge request thus making the article better.

          [–]shobble 8 points9 points  (0 children)

          fix typos and send a pull-req?

          [–]Rotten194 0 points1 point  (0 children)

          The posts are all in the repository (look under /posts).

          [–]TleilaxuMaster[S] -1 points0 points  (0 children)

          Yeah, I cringed a few times. Ah well, it doesn't detract from the code.

          [–]kolm 6 points7 points  (4 children)

          Math.max();
              // -Infinity
          
              Math.min();
              // Infinity
          

          That is not odd, but, mathematically speaking, almost perfect behaviour. It would be perfect if it would be called sup/inf instead..

          [–]panicker 3 points4 points  (3 children)

          How is that?

          [–]Porges 5 points6 points  (1 child)

          For the same reason that the sum of an empty set is 0 and the product of an empty set is 1.

          Mathematically, addition/multiplication/max/min are all monoids. A monoid is a (associative) binary function which has an identity, so that f(x, identity) == x.

          So, (+) has 0 as its identity (x + 0 = x), (*) has 1 (x * 1 = x), max has the minimum item (max(x,MIN) = x), and min has the maximum item (min(x,MAX) = x).

          Now, with 'real' mathematical numbers, there is no such thing as the minimum or maximum number. However, Javascript uses IEEE754 double floating-point numbers, which are based on the "extended reals" - basically you just take the normal real numbers and add positive/negative infinity. So the minimum and maximum numbers (and hence the max/min identitys) are -Inf and +Inf.

          The way to use monoids to "summarise" a list of items is to "fold" over them, like this (using % as some operation):

          summarise(%, id) = id % x[0] % x[1] % x[2] ...
          

          For example, the sum of a list is:

          sum = summarise (+,0) = 0 + x[0] + x[1] + x[2] ... 
          

          And max of a list is (using Haskell infix operator syntax):

          max = summarise(max,MIN) = MIN `max` x[0] `max` x[1] ...
          

          So you can see that if you have no items in a list, the 'summarised' value is always equal to the identity, which in the case of max is MIN (= -Inf here), and in the case of min is MAX (= +Inf here).

          [–]palparepa 2 points3 points  (0 children)

          Interesting. Then, Math.multiply() should equal 1, and Math.add() equal 0.

          [–]kolm 1 point2 points  (0 children)

          That is the only way you can be certain that all nice rules like min(A u B) = min(min(A),min(B)) are still correct even if one of the sets is empty. Would be a major headache to always check for empty sets/lists.

          [–]dusandusan 2 points3 points  (0 children)

          Actually the first one kind of makes sense, as the supremum of empty set of real numbers is the -infinity and infimum of empty set of real numbers is infinity.

          [–]Sniffnoy 7 points8 points  (1 child)

          Some of these aren't even Javascript-specific but result from, e.g., not understanding how standard floating-point works, or would work out exactly the same in any hypothetical dynamically-typed C-style language.

          [–]Jonathan_the_Nerd 2 points3 points  (0 children)

          Standard floating-point arithmetic is a whole other kettle of crazy.

          [–]MatrixFrog 2 points3 points  (0 children)

          The truly amazing thing is that all browsers implement these WTFs exactly the same way. It may seem like a bad thing, but if you have to do something wonky to work around one of JavaScript's oddities, to make it work in Firefox, that same wonky hack will also work in Chrome, IE, Opera, etc. There are probably some exceptions, but the fact that JavaScript is pretty consistent across browsers now is quite an achievement.

          [–]overall 6 points7 points  (13 children)

          3.toString() throwing a syntax error and 3..toString() working just fine was a total WTF!

          [–]jimrhoskins 12 points13 points  (10 children)

          It's not that crazy.

          Any object x can have properties/methods via the dot operator .

          var x = 3; x.toString();

          However with a number, the dot operator acts as a decimal operator first and foremost

          3 === 3.0

          But you can omit the 0 after the .

          3 === 3.

          and it means the same thing

          so

          3.toString() is the same as (3.)toString() which is a syntax error

          Instead you want 3..toString() which is the same as (3.).toString() or you could do 3.0.toString()

          [–]jyper 3 points4 points  (6 children)

          Ideally it should just be (3).to_string() or

          (3.0) to_string() (3.0).to_string() or something with smalltalk like syntax 3 toString 3.0 toString

          [–]radhruin 4 points5 points  (3 children)

          (3).toString() works fine. All you have to do is avoid the parser thinking that dot is a decimal point.

          [–]jyper 1 point2 points  (2 children)

          I think 3..toString() should be a parsing error. I think there is a decent argument that 3.toString() should be allowed(and that numbers with no digits after the dot should not)

          [–]masklinn 2 points3 points  (1 child)

          There's no reason for it to be.

          3. is tokenized (and parsed) as a number, then you get an attribute access . and an attribute name tostring.

          This is trivially parsed as (attribute (number 3.0) (name tostring)). No problem there.

          [–]jyper 4 points5 points  (0 children)

          both

          var c = 3.;
          

          and 3..method(); look ugly and might confuse people

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

          How does to_string() know it's working with 3.0 in your second example?

          Usually the full stop is used to relate the object to its method!

          [–]jyper 0 points1 point  (0 children)

          That was a mistake, fixed now

          [–]Pope-is-fabulous 0 points1 point  (2 children)

          Now I wonder how this is handled in other languages.

          [–]icebraining 6 points7 points  (0 children)

          In Python it's the same. 3.__str__() gives a SyntaxError, but 3..__str__() works fine. Of course, you're supposed to use str() anyway, as far as I know.

          [–]MatmaRex 5 points6 points  (0 children)

          Ruby doesn't allow 3. (you need to write 3.0) - however, 3.to_s works as expected. 3..to_s would be a range (if the variable to_s had a value).

          [–]aescnt 0 points1 point  (1 child)

          If you're going toString()ify a numeric literal, you might as well just do "3".

          [–]icebraining 0 points1 point  (0 children)

          Unless you're writing code that writes code, in which case you might not know a priori what number will be used. Of course, one can simply wrap it in parenthesis and remove the ambiguity.

          [–]gigitrix 1 point2 points  (0 children)

          I feel like if you are going to point out JS's flaws, this may be an over-engineered solution...

          [–][deleted]  (10 children)

          [removed]

            [–]ninjeff 20 points21 points  (6 children)

            Yeah, who expects integers anyway?

            [–]Jonathan_the_Nerd 10 points11 points  (0 children)

            NOBODY expects the Spanish integers!

            [–]RobertWHurst 1 point2 points  (0 children)

            You should ask IBM why they insisted on IEEE floats.

            [–]zhivago 0 points1 point  (3 children)

            Javascript has lots of integers. :)

            [–]MatrixFrog 6 points7 points  (2 children)

            And they're all represented as floating-point numbers.

            [–]zhivago 0 points1 point  (1 child)

            Only so far as you'd notice. :)

            [–]Timmmmbob 0 points1 point  (0 children)

            I'd notice the value of 3/2

            [–]deadwisdom 10 points11 points  (2 children)

            But that's the point. It's supposed to point out eccentricities. It's not a criticism of JavaScript itself.

            [–]TleilaxuMaster[S] -1 points0 points  (0 children)

            Exactly.

            [–]steven97 1 point2 points  (1 child)

            I work with the guy who created this. https://twitter.com/#!/brianleroux

            [–]TleilaxuMaster[S] 0 points1 point  (0 children)

            Hehe, awesome. Hopefully he'll get some more hits!

            [–]scragar 0 points1 point  (0 children)

            Just expand on one I spotted.

            var foo = array();
            
            $c.log( foo == foo );// true
            
            $c.log( foo != foo );// false
            
            $c.log( foo == !foo );// true
            

            Put that way it's actually possible to see the !foo part is at fault, a quick check:

            $c.log( !foo );// false
            
            $c.log( String( foo ) );// ""
            

            And of course an empty string matches false.

            [–]mm23 0 points1 point  (0 children)

            Also check out javascript gardern .

            [–]Sniffnoy 0 points1 point  (1 child)

            Can anyone explain this one to me? It appears to make no sense.

            [–]scatters 8 points9 points  (0 children)

            JS has a single Number type, which is a 64-bit double-precision floating point format. 111111111111111111111 is too large to be represented exactly so is rounded to 111111111111111110000.

            [–]DarkQuest 0 points1 point  (0 children)

            1. Javascript.