Numbers and dots

may 1 , 2012

A JavaScript syntax oddity with Numbers and Dots:


    // Ok, you may have seen wrapping a number literal with parens to call Number methods
    (42).toFixed(2); // "42.00"

    // And it is understandable that without parens you get a syntax error
    42.toFixed(2); // SyntaxError: identifier starts immediately after numeric literal

    // But numbers with decimals work fine without parens (WTF?)
    42.888.toFixed(2); // "42.89"

    // And whole numbers with two dots work as well (WTF?)
    42..toFixed(2); // "42.00"

    // Hey, so what about 3 dots!? Firefox gives XML-related error (WTF?)
    42...toFixed(2); // TypeError: XML descendants internal method called on incompatible Number

kensnyder via @ryanflorence


changing variables changes arguments

apr 3 , 2012

It turns out that changing the value of an argument variable will change its value in the arguments "array":


    > function hello(what) {
    .     what = "world";
    .     return "Hello, " + arguments[0] + "!";
    . }
    > hello("shazow")
    "Hello, world!"

This is documented behaviour (see NOTE 1 in §10.6 Arguments Object of ECMA-262).

I suspect, without evidence, that this allows named argument value lookups to be transformed at compile time into a lookup into the arguments array (so, for example, the what = "world"; would be transformed into arguments[0] = "world"; much like Python's fast locals).

@wolever


the universe answers and JavaScript still makes us wtf

nov 5 , 2011

This installment of wtfjs has to do with implicit type conversion during arithmetics and operator ambiguity / overloading.

If you can figure out what this returns on the first guess, I'll give you a great big hug the next time I see you:


    // is there an error (if not, what do I return?)
    "3" -+-+-+ "1" + "1" / "3" * "6" + "2"

Now, I won't spoil the fun for those of you at home trying to do this mentally, but the answer probably isn't what you'd expect. Go ahead, run it in a JavaScript console - WTF?! ^)*&#) right?

Firstly, I'm not sure about you guys, but I forgot some basic stuff from elementary school math - order of operations. One usually doesn't look at these when doing (supposed) string concatenation (but we're doing a little bit more than that, aren't we?).

The precedence of operators in JavaScript are (from highest to lowest):

delete void typeof ++ -- + - ~ !

       *
       /
       %

          +
          -
          <<
          >>
          >>>

               <
               >
               <=
               >=
               instanceof
               in

                          ==
                          !=
                          ===
                          !==

                               &
                               ^
                               |

                                  &&
                                  ||

                                      ? :

                                           =
                                           *=
                                           /=
                                           %=
                                           +=
                                           -=
                                           <<=
                                           >>=
                                           >>>=
                                           &=
                                           ^=
                                           |=

http://es5.github.com/#x11.4.6

First you'll notice that the + occurs twice (already a bit confusing), but additionally can be used for string concatenation based on contextual cues and type hints (i.e. 5 + "2" = "52", not 7).

11.6.1 The Addition operator ( + ) The addition operator either performs string concatenation or numeric addition.

[snip]

If Type(lprim) is String or Type(rprim) is String, then

Return the String that is the result of concatenating ToString(lprim) followed by ToString(rprim)

[Else,]

Return the result of applying the addition operation to ToNumber(lprim) and ToNumber(rprim).

So this means we'll have to find out what which + is doing what.

Additionally, you can throw a unary + or - operator onto UnaryExpressions like candy to change their signs and make things prettier and easier to understand for your fellow developers. Note that every - flips the sign, but + does essentially nothing functionally to the result of the UnaryExpression (i.e. +1 === 1).

NOTE: Unary + can be useful to cast to numbers (hence the +new Date hack we often see to emulate Date.now()) and to guarantee you're casting to decimal (i.e. +"013" is 13 but parseInt("013") is 11). This method has the drawback, however, that it can't handle letters in the string (i.e. +"1a" gives NaN whereas parseInt("1a") gives 1, and accidental octal conversion number guessing by parseInt can be fixed by adding a base to parseInt (i.e. parseInt("08", 10) is 8).

http://es5.github.com/#x11.6.1

These unary operators are evaluated first as they have the highest precedence, so we start with the result of +-+-+"1" (notice how the first - is treated as the operator when evaluating an AdditiveExpression). The result of this is simply 1.

NOTE: This ignores your grammar teacher's rules about double negatives.

Let's make this line into an expression tree. After unary operators are evaluated, we have:


              +

         "2"         +

              *              -

          /     "6"      1      "3"

     "1"     "3"

However, we don't know what types are implicitly cast yet. Let's make another tree showing non-ambiguous operations (/, *, -):


              +

         "2"         +

              *              -

          /      6       1       3

      1       3

This greatly simplifies the bottom of the tree as they're all now numbers. We continue evaluating from bottom left most child up, which evaluates the expression as so: (3-1)+((1/3)*2) = 2 + 2 = 4. This leaves us with this tree:


          +

     "2"      4

This last operation is ambiguous, so we look to see if either operand is a string (which "2" is), so the type of lprim or rprim is String after calling ToPrimitive (which rprim is), so the final result is


    "42"

(which is the obvious answer to life the universe and everything, really).

@danbeam


undefined identity crisis

nov 2 , 2011

Everyone loves Yoda Conditionals, right? They stop you from accidentally assigning stuff when you should be comparing, breaking everything! Or do they?


    function test (a) {
      if (undefined = a) {
        a = {}
      }
      return a;
    }
    undefined === test(null)

I encountered this gem recently. It's more a bug in the code than the implementation, but it's the implementation that allows this quirky disaster to occur. A single missing equals symbol in the comparison of undefined against a is the source of the chaos.

When the type equality comparison is evaluated, the state of undefined is stored, then test is executed and it's return value is stored, then the comparison is made. Before running test, undefined will be what we expect; undefined. But after running test, it will now be null. Because the value of undefined was stored for the comparison before running test, the first time running this will return false, while the second time will return true. Undefined identity crisis!

By @stephenbelanger


Fork me on GitHub