{}+{} incosistency
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 guess you are confused by a Chrome DevTools feature bugs.chromium.org/p/chromium/issues/detail?id=499864
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
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 callObject.prototype.valueOf()
, which returnsthis
(see 19.1.3.7). Following this, we calltoString()
, 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.
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`
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>:
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
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.
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:
- "{}": expression context; claims to be an Object.
- " {}": expression context.
- "var x = 1": statement context; defines a property named "x" on the global.
- "let x = 1": statement context; creates a binding for "x".
- "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.
If you see this then you will understand.
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:
- "{}": expression context; claims to be an Object.
- " {}": expression context.
- "var x = 1": statement context; defines a property named "x" on the global.
- "let x = 1": statement context; creates a binding for "x".
- "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.
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
if "{}+{}" is a statement, than how can it return (or yield) a result?
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.
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
Short question - code {}+{} V8 result: "[object Object][object Object]" Safari, Firefox, Edge: NaN
Which one is correct?