Why does a semicolon affect object initialization in es6? - javascript

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).

Related

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

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!

Integer by itself in a javascript file

I found a typo in one of my javascript files where there was an integer just sitting on a line on its own with nothing else. The file never errored out because of it so I was wondering, how does javascript handle or evaluate a line with just an integer on it and nothing else 'behind the scenes'?
The following is a valid statement:
4;
Of course, it would make more sense to do something like var num = 4;, but what you're doing above is the same thing, you're just not saving it's return value to a variable.
You can even have completely empty statements. The following is a valid, empty statement:
;
So you could have a program that looks like this:
var num = 4;
4;
;
Each of those lines would be valid.
Well, "5" is a code, that returns 5. You can try it out by opening Chrome Dev Tools (Press F12), then open console and enter a number. It will return a number, so it is valid piece of code.
You can write any expression in a line, in fact, it is referenced an ExpressionStatement in the grammar. So the line 4; is a statement, which evaluates, but never sotres it's variable; You can even leave the semicolon in most cases because of Automatic Semicolon Insertion.
These statements get evaluated, but then the resulting value is ignored, meaning that single number or string literals will not have any side effect (like 1+2+3;). However, calling functions or accessing variables or fields can have side effects (like: 1+a()+b), a gets accessed and called, b gets accessed).
But there are special cases, like "use strict";, which old engines just skip over as it is just a StringLiteral inside an ExpressionStatement, but modern browsers notice this line, and switch to strict mode.

Does javascript use some sort of implicit "noop" call?

This is probably JS 101 but...
Can someone with a better understanding of JS engines explain to me why string literals, integers and so forth are 'ignored' by JS or treated as otherwise valid code?
JS Hint does give 'unexpected expression' reports however the code remains valid and runs.
I've created the following pen to, hopefully, explain what I mean here.
http://codepen.io/billythekid/pen/zyGbi/
var span = document.getElementsByTagName('SPAN')[0];
// let's show what I'm trying to say here by way of an expanded example.
function foo()
{
var bar = "something";
}
foo(); // does nothing useful, but returns nothing either - valid and understandable;
function baz()
{
return "nothing"; // a string
}
function zoo()
{
return 250; // an integer
}
var a = baz(); // the variable holds the return value. The function is evaluated and the return value is assigned to the variable.
span.innerHTML += a+"<br>";
baz(); // produces no error despite the function returning a value. Why doesn't the JS engine see this as the evaluated string "something" and try to run the string as a JS command?
span.innerHTML += "this code has run, so the JS didn't break above. Why wasn't the returned string parsed and invalid?<br>";
"something"; // the string literal
span.innerHTML += "this code has run, so the JS didn't break above. So why not? How is a string literal valid JS? Why no errors?<br>";
var b = zoo();
span.innerHTML += b+"<br>";
zoo();// produces no error despite the function returning a value. Why doesn't the JS engine see this as the evaluated integer 250 and try to run the string as a JS command?
span.innerHTML += "this code has run, so the JS didn't break above. So why not? How is an evaluated integer valid JS? Why no errors?<br>";
250; // the integer literal
span.innerHTML += "this code has run, so the JS didn't break above. So why not? How is an integer literal valid JS? Why no errors?<br>";
eval(250); // the integer literal
span.innerHTML += "this code has run, so the JS didn't break above. So why not? How is an evaluated integer literal valid JS? Why no errors?<br>";
eval("something"); // the string literal
span.innerHTML += "this code broke, it can't run a string that's not been defined as a function or whatever.<br>";
// and, had the previous code not broken...
non_func(); // doesn't exist!
span.innerHTML += "this code doesn't run! So it'll error out with a call to a function/variable that doesn't exist but not eval'd code that isn't JS, such as a string, or even the literals of these objects!<br>";
// It appears that anythign not explicitly wrapped in an eval function is ignored by JS rather than throwing any errors. Why is this?
Simply running a string literal such as "foo"; as a line in the console seems to return itself.
Is the JS internally wrapping simple cases like these in some sort of 'noop' method or internally garbage-collecting such things or does it simply see the code as "run" once it's gone past it and has nothing more to do (such as assign values to a variable, or some other thing?
I got to thinking about this when using a setInterval() call, if I assign it's return value (well, it's ID identifier) to a var for using in a clearInterval later it's valid but it's also valid when we ignore the returned ID. The ID isn't "parsed" as JS.
Using strict mode seems to have no effect on this behaviour either.
Hopefully I've not made this more confusing than it needs to be. :oD
One big culprit behind your confusion is the C programming language. In it, many things that you think are statements, such as assignments, are actually expressions.
//This is valid C and Javascript code
x = (y += 1);
//We all have been bitten by this one once haven't we?
while(x = y){
}
and in order to let these statements be used in lines of their own, there is a rule in the language grammar that lets an expression followed by a semicolon to count as a statement
stmt :=
<if_then_else> |
<while loop> |
... |
<expression> ';'
The rule for evaluating these single-expression statements is that the expression is evaluated for side-effects and its value gets ignored.
Javascript (and lots of other languages) adopted lots of things from the C syntax, including this particular treatment of expressions. Using a string as a statement does nothing with its value (unless the string is "use strict" - then its a useful hack that does something in new browsers but nothing in the old ones). Using a function call as a statement runs it for side effects and ignores its return value. Some more stingy languages, such as Haskell or Go will complain if you ignore return values but C and Javascript will just throw them away.
-- In Haskell you get a compiler warning for ignoring return values
do
functionThatReturnsNothing()
a <- functionThatReturnsAValue()
_ <- functionThatReturnsAValue() -- unless you ignore it explicitly
It appears that anythign not explicitly wrapped in an eval function is ignored by JS rather than throwing any errors.
Thank god its that way! It would be crazy error prone if things got implicitly eval-ed like that and strings should never be run as code unless you are explicit about it!

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