17

In the Chrome JavaScript console, why does wrapping the statement {} - 0 in parentheses change the returned value?

{} - 0    // Returns -0
({} - 0)  // Returns NaN

It seems incredibly strange that wrapping a single statement in parentheses alters the contained value. What am I missing here?

Elliot Godzich
  • 216
  • 2
  • 12

3 Answers3

19

There are two possible interpretations of the line {} - 0:

  1. It can be interpreted as {}; -0, where {} is interpreted as an empty block statement, and - is the unary negation operator (so -0 is just "negative zero"). The value of this when evaluated is the value of the last statement, which is -0.
  2. It can be interpreted as ({} - 0), where {} is interpreted as an empty object, and - is the subtraction operator (so 0 is subtracted from {}).

In your first line, this is ambiguous, so it will choose the first interpretation. In the second line, the first interpretation is invalid (as a block statement can never be part of an expression, which you're forcing with the parantheses).

Frxstrem
  • 38,761
  • 9
  • 79
  • 119
  • 4
    This is a very interesting question. Not OP, but I have a followup to this: what if you force the `{}` to be interpreted as an object by adding a method or property? This works with the parenthesis but not without. (e.g. with parenthesis, this expression is evaluated to `3`: `({valueOf:()=>3} - 0)`, but without, it still returns `-0` in the console: `{valueOf:()=>3} - 0`). Shouldn't the added method force it to interpret the `{ .. }` as an object at that point and then try to evaluate it as subtraction? – Joseph Marikle Aug 11 '17 at 21:16
  • @JosephMarikle I think the same reasoning here still applies, without the parens, it can still be interpreted as `{valueOf:()=>3}; -0` however `({valueOf:()=>3}; -0)` is a syntax error. – Elliot Godzich Aug 11 '17 at 21:36
  • 6
    @JosephMarikle You would think so, but no: `valueOf:` is in this case a [label statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label) (a rare JavaScript feature), before the statement `() => 3;`, so it's still interpreted as a block statement instead of an object. It's interpreted the same way as `{ valueOf: () => 3; }; -0`. – Frxstrem Aug 11 '17 at 21:37
  • 2
    @JosephMarikle If you add multiple properties, however, it can't be a block statement anymore: `{ a: 1, b: 2 }` can't be a block statement because label statements can't occur after a comma (because they can't be part of an expression). – Frxstrem Aug 11 '17 at 21:42
  • 1
    @Frxstrem That is so interesting. Thanks for the explanation. Interestingly, after forcing the object with a second property/method, it just throws a syntax error (at least in Chrome). `{valueOf:()=>3,a:0} - 0`. – Joseph Marikle Aug 11 '17 at 21:48
  • @JosephMarikle Yes, I noticed. I can't exactly explain why (I don't know the details of how JS is parsed), but [this question](https://stackoverflow.com/q/26347326) seems to have some interesting related answers if you're interested in this. – Frxstrem Aug 11 '17 at 21:52
4

{} - 0: here {} is just an empty block that does nothing, so -0 returned by the console.

({} - 0): here {} is a part of expression and is converted to a number. There is no valueOf() method defined in that empty object and, while converting to a number, it falls back to toString() method which returns something like object Object for {}. Then this string object Object is being converted into a number and gives NaN since it is actually not a number. So, we've got

({} - 1) -> ('object Object' - 1) -> (NaN - 1)

and everything with NaN gives NaN. That's what you finally see in the console.

curveball
  • 4,320
  • 15
  • 39
  • 49
2
{} - 0

is interpreted: {} empty block statement and - 0 negative zero

({} - 0)

all inside () is interpreted as an expression, empty object - 0 = NaN

madox2
  • 49,493
  • 17
  • 99
  • 99