{}+{} incosistency

# Michał Wadas (8 years ago)

Short question - code {}+{} V8 result: "[object Object][object Object]" Safari, Firefox, Edge: NaN

Which one is correct?

# Caitlin Potter (8 years ago)

My read of section 12.8.3.1 (tc39.github.io/ecma262/#sec-addition-operator-plus-runtime-semantics-evaluation), is that V8 is getting this right.

ToPrimitive on {} will, by default, return “[object Object]” — We first call Object.prototype.valueOf(), which returns this (see 19.1.3.7). Following this, we call toString(), and get “[object Object]”.

Subsequent steps of the algorithm just concatenate the two strings together. So it looks like the other vendors are getting this wrong?

# ziyunfei (8 years ago)

I guess you are confused by a Chrome DevTools feature bugs.chromium.org/p/chromium/issues/detail?id=499864

# Caitlin Potter (8 years ago)

This isn’t a devtools feature, this is at a lower level, and impacts regular code (eg jsfiddle.net/jtbtjkLb, jsfiddle.net/jtbtjkLb)

The thing is, V8 is getting this right, at least in ecma262 6.0 and later. I haven’t checked ES5 to see if this has changed

# Till Schneidereit (8 years ago)

On Wed, Apr 13, 2016 at 4:32 PM, Caitlin Potter <caitpotter88 at gmail.com>

wrote:

My read of section 12.8.3.1 ( tc39.github.io/ecma262/#sec-addition-operator-plus-runtime-semantics-evaluation ), is that V8 is getting this right.

ToPrimitive on {} will, by default, return “[object Object]” — We first call Object.prototype.valueOf(), which returns this (see 19.1.3.7). Following this, we call toString(), and get “[object Object]”.

Subsequent steps of the algorithm just concatenate the two strings together. So it looks like the other vendors are getting this wrong?

I don't think that reading is right. Steps 5 and 6 call ToPrimitive without a PreferredType. For objects without a @@toPrimitive property, that ends up calling OrdinaryToPrimitive(input, "number"), which results in NaN. So step 7 isn't called - instead we end up running steps 8-10.

# Caitlin Potter (8 years ago)

Well, I think you’re wrong here:

OrdinaryToPrimitive:

Step 5: methodNames is [ “valueOf”, “toString” ]

• For each name in methodNames in List order, do
	• Let method be ? Get(O, name). << `Object.prototype.valueOf, Object.prototype.toString`
	• If IsCallable(method) is true, then
		• Let result be ? Call(method, O).
		• If Type(result) is not Object, return result. << Object.prototype.valueOf returns an Object, so we continue to `toString`
# Michał Wadas (8 years ago)

I can confirm - ziyunfei is right. Chrome console treats {}+{} as ({}+{}). The first one should be parsed as "empty block, unary plus operator, empty object literal". ({}+{}) is parsed as "empty object literal, addition operator, empty object literal".

({}+{}) yields "[object Object][object Object]" in all major vendors.

2016-04-13 17:19 GMT+02:00 Till Schneidereit <till at tillschneidereit.net>:

# ziyunfei (8 years ago)
# Allen Wirfs-Brock (8 years ago)

On Apr 13, 2016, at 8:11 AM, Caitlin Potter <caitpotter88 at gmail.com> wrote:

The thing is, V8 is getting this right, at least in ecma262 6.0 and later. I haven’t checked ES5 to see if this has changed

ES6 refactored ToPrimitive but the semantics (except for the @@toPrimitive hook) did not change from ES5.1 (or ES3, for that matter). The ES5 equivalent is at ecma-international.org/ecma-262/5.1/#sec-8.12.8, ecma-international.org/ecma-262/5.1/#sec-8.12.8

The V8 result is correct

# Boris Zbarsky (8 years ago)

On 4/13/16 12:04 PM, Allen Wirfs-Brock wrote:

The V8 result is correct

In an expression context.

It sounds like all the actual JS engines involved get all of this right; the only difference is whether evaluation in the browser's console happens in an expression context or in a statement context.

# Boris Zbarsky (8 years ago)

On 4/13/16 12:08 PM, Boris Zbarsky wrote:

It sounds like all the actual JS engines involved get all of this right; the only difference is whether evaluation in the browser's console happens in an expression context or in a statement context.

And in particular, it seems like most browsers evaluate console stuff in a statement context, but Chrome is somewhat inconsistent about it. Some experimentation suggests the following is happening, for various input strings:

  1. "{}": expression context; claims to be an Object.
  2. " {}": expression context.
  3. "var x = 1": statement context; defines a property named "x" on the global.
  4. "let x = 1": statement context; creates a binding for "x".
  5. "function f() {}": statement context; defines a property named "f" on the global.

I haven't experimented with more interesting whitespace preceding a '{' but it looks like the heuristic is to use expression context if the first non-whitespace character is '{' and to use statement context otherwise or something.

# ziyunfei (8 years ago)

If you see this then you will understand.

chromium.googlesource.com/chromium/src/+/4fd348fdb9c0b3842829acdfb2b82c86dacd8e0a/third_party/WebKit/Source/devtools/front_end/sdk/ConsoleModel.js#215

On 4/13/16 12:08 PM, Boris Zbarsky wrote:

It sounds like all the actual JS engines involved get all of this right; the only difference is whether evaluation in the browser's console happens in an expression context or in a statement context.

And in particular, it seems like most browsers evaluate console stuff in a statement context, but Chrome is somewhat inconsistent about it. Some experimentation suggests the following is happening, for various input strings:

  1. "{}": expression context; claims to be an Object.
  2. " {}": expression context.
  3. "var x = 1": statement context; defines a property named "x" on the global.
  4. "let x = 1": statement context; creates a binding for "x".
  5. "function f() {}": statement context; defines a property named "f" on the global.

I haven't experimented with more interesting whitespace preceding a '{' but it looks like the heuristic is to use expression context if the first non-whitespace character is '{' and to use statement context otherwise or something.

# Caitlin Potter (8 years ago)

Okay, I was under the impression OP was asking about a more serious incompatibility in actual code (and not in the dev-tools console), crisis averted :x

# Vic99999 (8 years ago)

if "{}+{}" is a statement, than how can it return (or yield) a result?

# Awal Garg (8 years ago)

Only the first {} is a statement. After that you have + which starts an expression, and +{} evaluates to NaN, which is, as expected, the result in all other browser devtools and repls.

# Alan Schmitt (8 years ago)

On 2016-04-13 19:53, Vic99999 <vic99999 at yandex.ru> writes:

if "{}+{}" is a statement, than how can it return (or yield) a result?

It returns a completion record, like every other construction (www.ecma-international.org/ecma-262/6.0/#sec-completion-record-specification-type). In most cases you don't get to see the value inside, but you can get to it through eval.

In fact, I often use eval when I don't know if a console is running my code as an expression or as a statement, as eval runs code as programs. Running eval("{}+{}") and eval("({}+{})") nicely shows the two correct results, which boils down to whether {} is a block (www.ecma-international.org/ecma-262/6.0/#sec-block) with a single empty statement, or if it's an object literal.

Alan