184

Why does object destructuring throw an error if there is no var keyword in front of it?

{a, b} = {a: 1, b: 2};

throws SyntaxError: expected expression, got '='

The following three examples work without problems

var {a, b} = {a: 1, b: 2};
var [c, d] = [1, 2];
    [e, f] = [1, 2];

Bonus question: Why do we not need a var for array destructuring?

I ran into the problem doing something like

function () {
  var {a, b} = objectReturningFunction();

  // Now a and b are local variables in the function, right?
  // So why can't I assign values to them?

  {a, b} = objectReturningFunction();
}
Ivar
  • 6,138
  • 12
  • 49
  • 61
alawatthe
  • 1,843
  • 2
  • 12
  • 5

4 Answers4

262

The issue stems from the {...} operators having multiple meanings in JavaScript.

When { appears at the start of a Statement, it'll always represent a block, which can't be assigned to. If it appears later in the Statement as an Expression, then it'll represent an Object.

The var helps make this distinction, since it can't be followed by a Statement, as will grouping parenthesis:

( {a, b} = objectReturningFunction() );

From their docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#assignment_separate_from_declaration_2

Notes: The parentheses ( ... ) around the assignment statement are required when using object literal destructuring assignment without a declaration.

{a, b} = {a: 1, b: 2} is not valid stand-alone syntax, as the {a, b} on the left-hand side is considered a block and not an object literal.

However, ({a, b} = {a: 1, b: 2}) is valid, as is var {a, b} = {a: 1, b: 2}

Your ( ... ) expression needs to be preceded by a semicolon or it may be used to execute a function on the previous line.

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Jonathan Lonowski
  • 121,453
  • 34
  • 200
  • 199
  • 1
    If it appears later in the Statement as an Expression, then it'll represent an Object. Does this mean we can never have block scope inside grouping parenthesis – sharad_kalya May 23 '18 at 06:59
  • 1
    This is a brilliantly obscure bit of knowledge that had me stumped. It allows destructuring across scope (well, sort of - you still need to declare your variables, of course). But it solves a constraint of using Promise chains while needing to use multiple variables resolved from one step of a Promise chain in another, without creating too much mess. Thanks. – Kevin Teljeur Oct 12 '18 at 14:28
57

If you write Javascript without semicolons, then the 'assignment without declaration' syntax should be preceded with a semicolon for it to work predictably

let a, b

;({a, b} = objectReturningFunction()) // <-- note the preceding ;

Just wanted to highlight this as it caught me out, and hopefully can save others some time figuring out why it doesn't work and/or produces weird results with code formatters like prettier.

Indeed, it's actually right there in the accepted answer (last line of the quoted docs) but easy to miss, especially without seeing an example!

davnicwil
  • 28,487
  • 16
  • 107
  • 123
14

Here's another way:

let {} = {a, b} = objectReturningFunction()

Pros:

  • No parenthesis needed
  • No semicolons needed
  • The extra assignment is a guaranteed no-op (given that no weird things are going on - also, your transpiler might not realize this)

Cons:

  • Looks a bit weird, although in my opinion no weirder than the !(){...}() IIFE.
  • Might be confusing as to why it's there. It is guaranteed to throw people off on the first encounter, so I would advise against using it as a one-off.
proto-n
  • 545
  • 6
  • 19
12

Consider:

colors = { r: 204, g: 51, b: 102, hex: "#cc3366" };

Here's a gist of some of the ways of destructuring:

Destructuring to new variables

let { r, g, b } = colors;
// initializes variables r, g, b

Destructuring to new variables with different names

let { r: red, g: green, b: blue } = colors;
// initializes variables red, green, blue

Destructuring to existing variables

let r, g, b;
...
({ r, g, b } = colors);

Destructuring to existing variables with different names

let red, green, blue;
...
({ r: red, g: green, b: blue } = colors);

Destructuring into another object with same property names

let myColor = { r: 0, g: 0, b: 0 };
...
({ r: myColor.r, g: myColor.g, b: myColor.b } = colors);

Destructuring into another object with different property names

let myColor = { red: 0, green: 0, blue: 0 };
...
({ r: myColor.red, g: myColor.green, b: myColor.blue } = colors);
SNag
  • 17,681
  • 10
  • 54
  • 69