2

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"
Nate Lipp
  • 700
  • 1
  • 7
  • 9
  • I don't see the difference, but the point of contention is that `{key}` can either be a shorthand object literal (if read in an expression context) containing a single key or it can be a block containing a single statement `{ key }` the same way you can do `{ i++ }` – Benjamin Gruenbaum Apr 25 '19 at 18:07
  • Where are you executing the above code? Are you using something like Babel to convert it to JS? – Todd Chaffee Apr 25 '19 at 18:14
  • There is no Babel involved this happens when run in the chrome web developers tool console. – Nate Lipp Apr 25 '19 at 18:21
  • This does not happen in FireFox. Seems like Chrome is trying to help you understand how ES6 would evaluate it using shorthand property names. Not sure why the semicolon changes that, but it might have to do with the fact that it becomes a statement rather than an expression? – Todd Chaffee Apr 25 '19 at 18:33

2 Answers2

2

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.

Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
0

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

Chris Baker
  • 49,926
  • 12
  • 96
  • 115
  • This is false. `{key1};` as a statement is not an object literal, it's a block. The console does special magic to wrap it in `()`s - see https://stackoverflow.com/questions/17268468/why-is-nan-only-on-the-client-side-why-not-in-node-js/17269376#17269376 for why/how this expands. – Benjamin Gruenbaum Apr 25 '19 at 18:23
  • @BenjaminGruenbaum And this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#New_notations_in_ECMAScript_2015 – Chris Baker Apr 25 '19 at 18:27
  • That's true, `({foo})` is an object literal containing a single member, `{foo};` as a statement on the other hand is a block containing `{foo}`, it's the same as `foo` without the braces since the new block has no meaning in this case (but it's still there). – Benjamin Gruenbaum Apr 25 '19 at 18:28
  • Here is an article a developer I respect wrote that explains this clearly :] http://2ality.com/2012/09/expressions-vs-statements.html – Benjamin Gruenbaum Apr 25 '19 at 18:29
  • 1
    So what are you alleging is false here? It is a quirk of console, and Object initializer does not work in IE – Chris Baker Apr 25 '19 at 18:29
  • This answer fails to explain why this phenomenon happens - it is because in OP's post they are in fact _not_ creating an object literal but a block. That's why they're seeing that behavior. The reason the version with the semicolon doesn't work is because chrome can't wrap it with parenthesis and make it into an expression. – Benjamin Gruenbaum Apr 25 '19 at 18:34
  • 1
    A lack of the explanation you want to see but don't want to post yourself is not false. There is no falsehood in my answer, and I insist you retract the statement. The phrase "object literal" never appeared in my answer, nor did I reference or imply the concept. – Chris Baker Apr 25 '19 at 18:35
  • I am going to go ahead and disengage at this point as I don't think we're heading anything constructive. I only care about correct information being around :] This answer is still wrong and completely misses what OP is asking but if you're offended by all means feel free to flag the comments for a moderator that will gladly clean it up (as comments are transient by nature). I did not mean to offend you - only try to improve the state of our shared knowledge base on this site. Hope you have a great weekend :) – Benjamin Gruenbaum Apr 25 '19 at 18:39
  • Based on my testing, this *is* a quirk of Chrome console. Seems like without the semi-colon it evaluates it as an expression, and uses the shorthand property notation to result in an object. @BenjaminGruenbaum is wrong. – Todd Chaffee Apr 25 '19 at 18:42
  • @ToddChaffee this was _exactly_ my point though. I've posted an answer pointing this out. – Benjamin Gruenbaum Apr 25 '19 at 18:45
  • There was nothing wrong with @ChrisBaker's answer. Your answer just goes into more detail about *why and how* this happens. – Todd Chaffee Apr 25 '19 at 18:47
  • 1
    @BenjaminGruenbaum I'm not going to flag it, I am going to ask you to use language responsibly and not accuse me of disinformation. From your answer, "but specifically how the Chrome DevTools parses expressions" -- also known as "a quirk of the console." I appreciate your contribution, but I don't appreciate the confrontational manner in which you are engaging here. Again, please retract your inaccurate statement, for the sake of an accurate knowledge base. – Chris Baker Apr 25 '19 at 18:51
  • I'm not trying to be confrontational (sorry if it comes off that way!). The first line says "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." that part is false. If you do `{foo};` in a script that is _not_ an object initialiser - that is a block containing a single statement `foo` and not an object initializer. – Benjamin Gruenbaum Apr 25 '19 at 18:54
  • @BenjaminGruenbaum "This is false" is a confrontation when dealing with a peer in computer science, and your initial comment is not directed at this new and also inaccurate assertion. Your initial point of contention was related to whether this behavior is a console quirk, which it is. Not false. Further, if, in a script, you use `let foo = 'somevalue'; let bar = {foo};` you get an object '{foo: 'somevalue'}` because it is an object initializer, ECMA2015 shorthand. Also not false. I don't know -- next time just post your answer and don't worry about other answers? – Chris Baker Apr 25 '19 at 19:03
  • @ChrisBaker right, but in OP's case they are _not_ doing `let bar = {foo};` they are doing `{foo};`. My whole point is that the two are different. In the case of `let bar = {foo}` it _is_ an object intiialiser and in the case of `{foo};` it is not an object initializer. "This is false" is a factual assertion of how the language works. It is not an attack or confrontation - I apologize if it came off as one this medium isn't great for communication. If you do `{foo};` in a script you will not get an object literal back. – Benjamin Gruenbaum Apr 25 '19 at 19:06
  • You are welcome to hit me up in chat to discuss: https://chat.stackoverflow.com/rooms/17/javascript – Benjamin Gruenbaum Apr 25 '19 at 19:07
  • 2
    Saying "this is false" to seemingly discredit an entire correct answer that may have had one subtle and small misstatement in it does feel confrontational. I understood what @ChrisBaker meant, even if it's technically correct that `{foo};` all on its own is not an object initializer. Compare "this is false" with, "good answer, but one small correction...". Far less confrontational. – Todd Chaffee Apr 25 '19 at 19:14