{}+{}="[object Object]", but {}+[]=0? [duplicate] - javascript

I noticed today that Chrome 49 no longer outputs NaN when you type {}+{} into the console. Instead it outputs the string [object Object][object Object].
Why is this? Did the language change?

Chrome devtools now automatically wrap everything that begins with { and ends with } in an implicit pair of parentheses (see code), to force its evaluation as an expression. That way, {} creates an empty object now. You can see this if you go back through the history (↑), the previous line will be contained in (…).
Why? I don't know, but I could guess it reduces confusion for newbies that don't know of the block-vs-object-literal thing, and it's also more helpful if you just want to evaluate an expression.
And in fact that's the reasoning, as discussed in bug 499864. Pure convenience. And because node REPL had it as well (see code).

If you hit the up arrow after checking this, you'll notice that instead of {} + {} it displays ({} + {}), which results in "[object Object][object Object]".
In comparison, in Firefox, {} + {} still displays NaN, but if you do ({} + {}) it also displays "[object Object][object Object]".
So, it looks like Chrome is adding the surrounding parenthesis automatically when it sees this operation.

As of Chrome 54 with regards to the console:
Unfortunately, I added the Clippy quote myself. The console gives no information about what it has done for you.
The new rules are incredibly simple saving us the trouble of laboriously typing these 2 difficult charcters o= or 0, before pasting Object Literals into the console:
If you have code that starts with: optional whitespace,(no comments permitted) followed by a {;
and that code could be interpreted as an object;
and that object is followed by no other code, unless:
the code after the first object is a binary operator,
then there can be as many operations as you like including groupings
provided the final operator has an Object literal in the right hand position;
and that final Object has not been grouped in parens
and that code is not terminated with a semicolon
and there are no comments following the code (internal comments are permitted so long as they are not in the initial or final position)
then and only then will your JavaScript (which may or may not actually be valid code) will be re-intrepted as a valid Object. You will not be informed that your code has been reinterpreted.
{wat:1}),({wat:2} Is finally an error again.
{let i=0;var increment=_=>i++} is correctly allowed, finally, which is quite a nice way of doing closures.
However, the following is incorrectly an object, this is just as a convenience as mentioned by #Bergi, it interprets JS wrong to help you! The spec says it is a block with a labeled statement "foo" with a literal 1 that is not assigned to anything.
{foo:1}
The above should be the same as
if(1) {
foo: 1
}
The following is treated correctly as a block... because it has a comment in front of it!
//magic comment
{foo:1}
So is this:
{foo:1}
//also magic
This is an Object:
{foo:
//not so magic comment
1}
This is an error
//not so magic comment
{foo:1}.foo
So is this:
{foo:1}.foo
This is fine:
1..wat
undefined
so is this:
['foo'][0]
The next one is correctly interpreted as an object whacked into the expression position with a 0, which is generally how we unambiguously ensure we have an expression instead of a statement.
0,{foo:1}.foo
I don't get why they wrap the value in parens. JS has some ridiculous design decisions, but trying to make it behave nicer in this one situation isn't really an option, the console needs to run JS correctly, and we need to be confident that chrome isn't just guessing that it thinks we really meant it to do something else.
If you don't like comma operators you can use assignment
x = {foo:1}.foo
Because as it stands
{} + {} + {}
"[object Object][object Object][object Object]"
;{} + {} + {}
"NaN[object Object]"
Crazy and consistent I can deal with... crazy and inconsistent no thank you!

Related

Why does a semicolon affect object initialization in es6?

The only difference in the following code is a the semicolon.
Can anyone tell me why is the result different?
key = 'value' // "value"
{key} // {key: "value"}
{key}; // semicolon is only diff, "value"
This is actually not related to JavaScript or browsers but specifically how the Chrome DevTools parses expressions. If you run your code in a regular script you will not run into this behavior. I run into a bit of why in this answer.
Here is what's happening:
key = 'value' // "value"
this defines a variable (on the global scope in non-strict mode) and assigns it a value.
{key} // {key: "value"}
This is a block, the Chrome devtools sees this this and wraps the object. It runs a regular expression against your code and checks specifically if it looks like an object literal:
try {
// Check if the code can be interpreted as an expression.
parse('return ' + code + ';');
// No syntax error! Does it work parenthesized?
const wrappedCode = '(' + code + ')';
parse(wrappedCode);
return wrappedCode;
} catch (e) {
return code;
}
Which it can be interpreted as so {key} is converted to a ({key}) which is an object literal and works.
{key}; // semicolon is only diff, "value"
This on the other hand has a semicolon at the end, since the above code converts it to ({key};) which is invalid JavaScript the pre-processing code in the Chrome devtools enters the catch clause and returns the original code.
This is a statement and not an expression. Essentially parsed like:
{
key;
}
In JavaScript, every statement has a "secret" value and you are simply seeing the log result of the last value of the script the REPL is giving you - in this case the string.
Our testing around the office shows this to be a quirk of the console. If you try to use this construct, with or without the semicolon, in a script, or eval it, or assign it to a variable, all result in the object {key: "value"} in browsers that support object initializers.
Testing in Chrome, we see results like yours. In Firefox, it produces an object regardless of the semicolon. In IE, it outputs the string whether you have the semicolon at the end or not.
It's also worth noting that creating an object with this notation, {key} to produce {key: "value"}, doesn't work in IE. In Chrome and Firefox, the browser will infer your meaning and produce the object with object initilizers (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#New_notations_in_ECMAScript_2015), IE will ask you what you're doing with your life because it does not support them.
You can create a similar situation with the script below, which will operate in console and give you surprising results, but isn't valid in a script. Just try to anticipate what this will output in console!
key1 = 'key';
key2 = 'key2';
{ {key1, key2} }
Long story short, though, don't depend on this behavior in a real script. Define your objects using standard notation (or object initializers if you're polyfilled for IE).

Why is {}+{} in the same browser works have different results

I'm in the Google browser console run is like this
{}+{} => "[object Object][object Object]"
But I run in the source file is like this
{}+{} => NaN
I'm in the firefox browser console run is like this
{}+{} => NaN
I was preparing another answer that basically said, "I don't know but here are some tests that I done and got similar results". But suddenly I realized the real answer.
I think this is somewhat an undefined behaviour in javascript syntax. Chrome console developers maybe were aware of this and whenever you write {}+{} in console, it turns the code into ({}+{}). Try ({}+{}) or a={}+{} and you will get the same result everywhere which is "[object Object][object Object]". That is the result of applying plus operator to two objects in javascript.
When you write some code in snippet however, it is eval'ed as it is. And it calculates {}+{} as NaN. But NaN is not the result of adding two objects. {} is object constructor literal. But it is also used to scope. So what is evaluated is equal to:
{ // Open scope
// You can write any working code here
// Only the result of the last statement is printed to console
} // Close scope
+{} // Result of this is NaN
// + is the unary number cast operator not the binary addition operator
You can confirm this by using an operator that is only unary. Take this for example:
{}~{}
// Error in Chrome console as it is turned into ({}~{}) which is bad syntax
// -1 in eval as it is the result of ~{}

Weird JS syntax: math operator in function declaration

I just played with Twitter API and found very weird construction. It's division operator between function name and arguments passing.
c/({"count":8098,"url":"http:\/\/example.com\/"});
I supposed that this should throw parser exception, but it works − just returning NaN instead of undefined. Also it works similar with * and - operators, while + returns c.toString() (or .valueOf, I dunno).
Even more, it's really syntax error thrown if i don't pass object to function. Here's examples:
function c() {}
>> undefined
c
>> function c() {}
c/()
>> SyntaxError: Unexpected token )
c/({});
>> NaN
c+({})
>> "function c() {}[object Object]"
c({})
>> undefined
I ran this in Chrome, but I belive it works everywhere if Twitter put it in response.
So. Why this even work, and how it works exactly? I believe this somehow releated to function returning value and its implicit type coercion, but I still don't get it.
Your URL is not escaped in that API call so twitter reads it incorrectly and creates meaningless syntax.
If you set the URL parameter to an escaped value (via encodeURIComponent) it will produce c({some:"Object"}) which is a JSONP response - JSONP is inherently a function call and works by injecting a script tag.
So, twitter does not produce this response deliberately, you're passing it problematic parameters and it's not failing gracefully.
As for why dividing a function in an object is NaN - in JS generally math operators don't throw very often. JavaScript is telling you - you tried to perform a math operation that doesn't make sense and the result is not a number - NaN.
For the funky results:
c/()
Is just invalid syntax, simply put you're diving something by nothing, 5/() would fail equally well.
c/({})
Diving a function by an object makes no sense, so it is giving you "Not a Number". I can add a spec quote but I don't think there is much more to add beyond "There is no way to interperet this differently".
c+({})
The + operator performs string concatenation in addition to numeric addition - so what's happening here is that toString is called for both the function and the object and you see the concatenated result.
c({})
This is just the return value of the function call

No semicolon before [] is causing error in JavaScript

var a = [1, 2, 3, 4];
var b = [10, 20, 30, 40];
console.log([a, b].length)
[a, b].some(function(x) {
x.push(x.shift())
});
I was extremely surprised today when this code caused
[a,b].some(function(x){ x.push(x.shift()) });
^
TypeError: Cannot call method 'some' of undefined
Obviously the JavaScript 'auto semicolon insertion' is not working as expected here. But why?
I know you might recommend to use ; everywhere to avoid something like that, but the question is not about whether it is better to use ; or not. I would love to know what exactly happens here?
When I'm worried about semicolon insertion, I think about what the lines in question would look like without any whitespace between them. In your case, that would be:
console.log([a,b].length)[a,b].some(function(x){ etc });
Here you're telling the Javascript engine to call console.log with the length of [a,b], then to look at index [a,b] of the result of that call.
console.log returns a string, so your code will attempt to find property b of that string, which is undefined, and the call to undefined.some() fails.
It's interesting to note that str[a,b] will resolve to str[b] assuming str is a string. As Kamil points out, a,b is a valid Javascript expression, and the result of that expression is simply b.
In general, one could say that implicit semi-colon's can easily fail when defining an array on a new line, because an array defined on a new line is interpreted as a property access of the value of the expression on the previous line.
Javascript does only consider new lines to mark the end of a statement if not ending the statement after this new line would cause a parse error. See What are the rules for JavaScript's automatic semicolon insertion (ASI)? and EcmaScript 5 spec for the exact rules. (Thanks to Rob W and limelights)
What happens is the following:
The code get interpreted as
console.log([a,b].length)[a,b].some(function(x){ x.push(x.shift()) });
i.e. all as one statement.
Now parse the statement:
some is called on the value of console.log([a,b].length)[a,b]
the value of console.log([a,b].length)[a,b] is computed by taking the returned value of console.log([a,b].length) (undefined) and then trying to access the property with the name of the value of a,b.
a,b evaluates to the value of b (try it in your console). There's no property with the value of b of undefined, so the resulting value will be undefined as well.
There's no method some on undefined, hence the error.
JavaScript doesn't treat every line break as a semicolon. It usually treats line
breaks as semicolons only if it can’t parse the code without the semicolons. Basically, JavaScript treats a line break as a semicolon if the next non-space character cannot be interpreted as a continuation of the current statement. JavaScript - The Definitive Guide: 6th Ed. section 2.4
So, in your case, it is interpreting the line as something like
console.log([a,b].length)[a,b].some(function(x){ x.push(x.shift()) });
And that is the reason for error. JavaScript is trying to perform array-access on the results of console.log([a,b].length). Depending on the JavaScript engine and the return value of console.log, you might get different errors.
If it is the last statement of the function or flow, you can avoid ';' but it is recommended to put ';' at the end of the each statement to avoid such error.

why {key:'value'} is printing "value" in console

While I was testing saw a behavior of console(Chrome). if i type console.log({key:'value'}) is printing Object {key: "value"} which was expected by me. But when I directly typed {key:'value'} in console it printing 'value' only while I expected Object {key: "value"}.
Saw same behavior in IE10 and FF.
What is the reason behind this behavior?
It's because your object is instead being interpreted as a block statement with a single statement within consisting of a string literal preceded by a label.
// v---block----v
{key: "value"}
// ^--label
For the console to interpret it as object literal syntax, it needs to be part of an expression. If you wrap in parens, you'll get the expected result.
({key:"value"})
Side note:
Here's the really weird part. A statement is a statement because it doesn't return anything. So why does it return "value"?
In JavaScript, statements have something like a final value. (I don't remember exactly what it's called.) It isn't anything that's useful or reachable in program code, but when a program is evaluated, that final value will be returned to whatever evaluated it.
The same goes when you use eval() to evaluate a program. Its final statement value will be returned. Since code in a console is eval'd, it gets that final value and prints it.
That's because what you type is interpreted.
{key:'value'} is a block and returns "value". You'd have had the same result with just key:'value' or 'value'.
If you want an expression returning your object, type ({key:'value'}).

Categories

Resources