12

Can anyone explain, why the following happens with ES6 array destructuring?

let a, b, c
[a, b] = ['A', 'B']
[b, c] = ['BB', 'C']
console.log(`a=${a} b=${b} c=${c}`)

// expected: a=A b=BB c=C
// actual:   a=BB b=C c=undefined

http://codepen.io/ronkot/pen/WxRqXg?editors=0011

Sebastian Simon
  • 18,263
  • 7
  • 55
  • 75
ronkot
  • 5,915
  • 4
  • 27
  • 41
  • 5
    put `;` after each line – Pranav C Balan Jun 27 '16 at 09:26
  • yup, tested that, @PranavCBalan's comment works – Tschallacka Jun 27 '16 at 09:28
  • 3
    This is why ASI is a misfeature. – Ry- Jun 27 '16 at 09:31
  • True, @PranavCBalan's solution works. Wow, for me this is first case where the semicolons really matter in js code. I prefer semicolon-less code for cleaner syntax, but maybe I have to start to use them. Can anyone explain, why in this case the semicolons are really needed? – ronkot Jun 27 '16 at 09:36
  • 2
    @ronkot Almost any time you start a line with array indexer or parenthesis you're going to have an issue ([Example](https://jsfiddle.net/h32muwxk/)). Most devs who prefer semi-colonless start the line with a `;` to fix ASI related issues. – CodingIntrigue Jun 27 '16 at 10:52

4 Answers4

15

As others have said, you're missing semicolons. But…

Can anyone explain?

There are no semicolons automatically inserted between your lines to separate the "two" statements, because it is valid as a single statement. It is parsed (and evaluated) as

let a = undefined, b = undefined, c = undefined;
[a, b] = (['A', 'B']
[(b, c)] = ['BB', 'C']);
console.log(`a=${a} b=${b} c=${c}`);

wherein

  • [a, b] = …; is a destructuring assignment as expected
  • (… = ['BB', 'C']) is an assignment expression assigning the array to the left hand side, and evaluating to the array
  • ['A', 'B'][…] is a property reference on an array literal
  • (b, c) is using the comma operator, evaluating to c (which is undefined)

If you want to omit semicolons and let them be automatically inserted where ever possible needed, you will need to put one at the start of every line that begins with (, [, /, +, - or `.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Why did it turn `[b, c]` to a `[(b, c)]` actually? – zerkms Jun 27 '16 at 09:37
  • Wow, this is the real answer for the question.... I were thinking what happening there... Now it's clear.... – Pranav C Balan Jun 27 '16 at 09:37
  • @zerkms: Because I like introducing grouping operators… in this case to emphasise that it is not an array literal with two elements – Bergi Jun 27 '16 at 09:38
  • is `['A', 'B'][b, c]` will get property `b` and `c` of array elements ? – Pranav C Balan Jun 27 '16 at 09:40
  • After testing with several cases, Ithink `['A', 'B'][b, c]` ==> `['A', 'B'][c]` – Pranav C Balan Jun 27 '16 at 09:42
  • @PranavCBalan: no, `b, c` evaluates to the value of `c`, so it will assign to the property with the name `"undefined"` on the array – Bergi Jun 27 '16 at 09:43
  • Marked as correct answer due to nice explanation. Another correct answer mark would go to @oleg's great answer. – ronkot Jun 27 '16 at 09:58
  • For those interested, here is the documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Automatic_semicolon_insertion – 3limin4t0r Mar 25 '19 at 23:36
6

You've fallen into a trap of line wrapping and automatic semicolon insertion rules in JavaScript.

Take this example:

let x = [1, 2]
[2, 1]

It's the interpreted as:

let x = [1, 2][2, 1] // === [1, 2][(2, 1)] === [1, 2][1] === 2

That weird [(2, 1)] thing above is related to how Comma Operator works.

Thus, your example:

let a, b, c
[a, b] = ['A', 'B']
[b, c] = ['BB', 'C']
console.log(`a=${a} b=${b} c=${c}`)

Is interpreted as:

let a, b, c
[a, b] = ['A', 'B'][b, c] = ['BB', 'C']
console.log(`a=${a} b=${b} c=${c}`)

Now, if you insert a semicolon, it will work as you intended:

let a, b, c
[a, b] = ['A', 'B']; // note a semicolon here
[b, c] = ['BB', 'C']
console.log(`a=${a} b=${b} c=${c}`)

Also, it's a good idea to check your code by pasting it into Babel repl to see the generated output:

'use strict';

var a = void 0,
    b = void 0,
    c = void 0;

var _ref = ['A', 'B'][(b, c)] = ['BB', 'C'];

a = _ref[0];
b = _ref[1];

console.log('a=' + a + ' b=' + b + ' c=' + c);
Oleg
  • 9,341
  • 2
  • 43
  • 58
2

I believe you have forgotten the line breaks ';'. Below is the corrected code. Please try:

let a,b,c
[a, b] = ['A', 'B'];
[b, c] = ['BB', 'C'];
console.log(`a=${a} b=${b} c=${c}`)
zubair1024
  • 843
  • 8
  • 24
-1
let a, b, c
[a, b] = ['A', 'B']***;***
[b, c] = ['BB', 'C']
console.log(`a=${a} b=${b} c=${c}`)

console: a=A b=BB c=C

Tschallacka
  • 27,901
  • 14
  • 88
  • 133
qunshan
  • 57
  • 3