5

I was checking out node.green and under destructuring, assignment > nested rest, the following example function is used:

function f() {
  var a = [1, 2, 3], first, last;
  [first, ...[a[2], last]] = a;
  return first === 1 && last === 3 && (a + "") === "1,2,2";
}

console.log(f())

Now, I understand destructuring but I cannot figure out why how a was rewritten to [1, 2, 2]

Also [...[a[2], last]] = a; returns [1, 2, 1]

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Bond
  • 184
  • 2
  • 9

2 Answers2

5
[first, a[2], last] = a;

is like

// first == undefined,   last == undefined,   a == [1,2,3]
first = a[0];
// first == 1,           last == undefined,   a == [1,2,3]
a[2] = a[1];
// first == 1,           last == undefined,   a == [1,2,2]
last = a[2];
// first == 1,           last == 2,           a == [1,2,2]

[first, ...[a[2], last]] = a;

is like

// first == undefined,   last == undefined,   a == [1,2,3],   tmp == undefined
first = a[0];
// first == 1,           last == undefined,   a == [1,2,3],   tmp == undefined
tmp = [a[1], a[2]];
// first == 1,           last == undefined,   a == [1,2,3],   tmp == [2,3]
a[2] = tmp[0];
// first == 1,           last == undefined,   a == [1,2,2],   tmp == [2,3]
last = tmp[1];
// first == 1,           last == 3,           a == [1,2,2],   tmp == [2,3]

[...[a[2], last]] = a;

is like

// last == undefined,   a == [1,2,3],   tmp == undefined
tmp = [a[0], a[1]];
// last == undefined,   a == [1,2,3],   tmp == [1,2]
a[2] = tmp[0];
// last == undefined,   a == [1,2,1],   tmp == [1,2]
last = tmp[1];
// last == 2,           a == [1,2,1],   tmp == [1,2]
Oriol
  • 274,082
  • 63
  • 437
  • 513
  • I guess you meant `tmp = [a[1], a[2]]` in your 2nd example, right? What precedence has the spread operator - is it the same as the bracket property accessor? –  Sep 04 '16 at 09:22
  • @LUH3417 Spread is not an operator, it's part of the literal syntax - like the brackets or the commas. – Bergi Sep 04 '16 at 12:16
  • @LUH3417 Yes,thanks. See [operator precedence](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence) in MDN. Property accession happens before spreading. – Oriol Sep 04 '16 at 12:19
  • @Bergi Do you mean that it depends on the context, if spread is an operator (`f(...args)` vs. `[...items]`)? I just aks because it is frequently termed an [operator](http://exploringjs.com/es6/ch_parameter-handling.html#sec_spread-operator) –  Sep 04 '16 at 19:43
  • 1
    @LUH3417 [It's not an operator indeed](http://stackoverflow.com/a/37152508/1048572). – Bergi Sep 04 '16 at 20:36
3

In the second line, you are setting a[2] to whatever is the second value of a, i.e. a[1], which in this case is 2.

Interestingly, if you change [first, ...[a[2], last]] to [first, a[2], last], last becomes 2 (the final values of first and a are the same as in your example):

var a = [1, 2, 3], first, last;
[first, a[2], last] = a;

console.log(first === 1);
console.log(a + '' === '1,2,2');
console.log(last === 2);

It looks like a[2] is being assigned the value 2 first, then last is being assigned the value of a[2]. In your example, it seems the old values of a are being kept. I suspect this is because ... creates an intermediate array, whose values are then being assigned to a[2] and last. Hopefully someone who knows the deep parts of the ES6 spec can confirm, but presumably that is the reason for the more convoluted [first, ...[a[2], last]].

Update

Bergi shared a spec reference in the comments:

see IteratorBindingInitialization, with BindingRestElement. However, the usage of a BindingPattern instead of a simple BindingIdentifier is actually new in ES7. In ES6 you'd need to write var [first, ...tmp] = a; [a[2], last]] = tmp; which makes clearer what is happening.

Frank Tan
  • 4,234
  • 2
  • 19
  • 29
  • @Bergi Could you share a link? – Frank Tan Sep 01 '16 at 19:52
  • 2
    See [IteratorBindingInitialization](http://www.ecma-international.org/ecma-262/6.0/#sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization) with `BindingRestElement`. However, the usage of a `BindingPattern` instead of a simple `BindingIdentifier` is actually [new in ES7](http://www.ecma-international.org/ecma-262/7.0/#prod-BindingRestElement). In ES6 you'd need to write `var [first, ...tmp] = a; [a[2], last]] = tmp;` which makes clearer what is happening. – Bergi Sep 01 '16 at 20:02