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'}).
Related
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).
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!
I'm a beginner and just successfully trouble-shoot my code. I'm glad that I found it, however it took me a long time. I'm hoping to learn why it happened.
Here's the buggy original code. Assume that the variable [nextAlpha] has already been assigned a string value:
nextAlpha.toUpperCase();
Through some creative testing I was able to determine it was the line causing issues. I thought perhaps it's not actually updating the value of variable [nextAlpha]. I tried this instead, and it worked:
nextAlpha = nextAlpha.toUpperCase();
I've left the rest of my code out, but assume that [var = nextAlpha] has already been declared at the top of my script, which I think means "globally." With that information, I thought it was enough to simply call the method on the variable. Why doesn't this "update" the string to upper case like it does when I go the extra step to (re)assign it to the original [nextAlpha] string?
toUpperCase returns the converted string as a new object - it does not perform the conversion on nextAlpha.
From the Mozilla reference:
The toUpperCase method returns the value of the string converted to uppercase. toUpperCase does not affect the value of the string itself.
reference
In JavaScript, Strings are immutable:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures
Unlike in languages like C, JavaScript strings are immutable. This means that once a string is created, it is not possible to modify it. However, it is still possible to create another string based on an operation on the original string
toUpperCase() is a function (so return a value) not a property (affect the variable itself)
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.
So I just mistakenly typed something in a JS console and was surprised that it didn't give me a syntax error. My question: Why is this valid?
function foo(){
{
console.log('Valid Function');
}
}
foo();
I would assume that it would interpret the inner { ... } to be an object literal, but if that were the case the console.log wouldn't be valid... so...?
This is an anonymous code block. For example:
{var x=5; alert(x);}
Furthermore to clarify your question "to be an object literal, but if that were the case the console.log wouldn't be valid... so...?"
It is NOT an object. However, note that if you had a key: or something it could be interpreted as an object and still work, since statements which don't return anything will be interpreted as undefined.
({1:console.log('test')})
result (also prints out 'test'):
Object
1: undefined
__proto__: Object
According to this javascript 2.0 grammar:
http://www.mozilla.org/js/language/js20-2000-07/formal/parser-grammar.rtf
An object literal is defined here:
ObjectLiteral
{ } | { FieldList }
Which means it must be empty or contain a valid field list which must have a bare colon to separate the key and value.
Your block is therefore interpreted as a "Statement" which can have the braces around it.
Lots of c syntax languages have arbitrary braces to group statements, but unlike most of them, JavaScript does not limit variable scope of those variables declared in that block. This means that the extra braces do not really have much purpose, are almost never used and this is probably why you didn't expect it to work!
Braces specify a block of code. A block is a valid statement, and can appear wherever a statement is allowed. See http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf page 220.