Destructuring in JavaScript
One new feature in JavaScript, introduced in ECMAScript 6 (formally ECMAScript 2015, but it’ll always be ES6 in our hearts), is destructuring. Destructuring is syntactic sugar for assigning sub-values within a single value — nested properties, iteration results, &c., to arbitrary depths — to a set of locations (names, properties, &c.).
// Declarations var [a, b] = [1, 2]; // a = 1, b = 2 var { x: c, y: d } = { x: 42, y: 17 }; // c = 42, d = 17 function f([z]) { return z; } print(f([8675309])); // 8675309 // Assignments [b, f.prop] = [3, 15]; // b = 3, f.prop = 15 ({ p: d } = { p: 33 }); // d = 33 function Point(x, y) { this.x = x; this.y = y; } // Nesting's copacetic, too. // a = 2, b = 4, c = 8, d = 16 [{ x: a, y: b }, [c, d]] = [new Point(2, 4), [8, 16]];
Ambiguities in the parsing of destructuring
One wrinkle to destructuring is its ambiguity: reading start to finish, is a “destructuring pattern” instead a literal? Until any succeeding =
is observed, it’s impossible to know. And for object destructuring patterns, could the “pattern” just be a block statement? (A block statement is a list of statements inside {}
, e.g. many loop bodies.)
How ES6 handles the potential parser ambiguities in destructuring
ES6 says an apparent “pattern” could be any of these possibilities: the only way to know is to completely parse the expression/statement. There are more elegant and less elegant ways to do this, although in the end they amount to the same thing.
Object destructuring patterns present somewhat less ambiguity than array patterns. In expression context, {
may begin an object literal or an object destructuring pattern (just as [
does for arrays, mutatis mutandis). But in statement context, {
since the dawn of JavaScript only begins a block statement, never an object literal — and now, never an object destructuring pattern.
How then to write object destructuring pattern assignments not in expression context? For some time SpiderMonkey has allowed destructuring patterns to be parenthesized, incidentally eliminating this ambiguity. But ES6 chose another path. In ES6 destructuring patterns must not be parenthesized, at any level of nesting within the pattern. And in declarative destructuring patterns (but not in destructuring assignments), declaration names also must not be parenthesized.
SpiderMonkey now adheres to ES6 in requiring no parentheses around destructuring patterns
As of several hours ago on mozilla-inbound, SpiderMonkey conforms to ES6’s parsing requirements for destructuring, with respect to parenthesization. These examples are all now syntax errors:
// Declarations var [(a)] = [1]; // BAD, a parenthesized var { x: (c) } = {}; // BAD, c parenthesized var { o: ({ p: p }) } = { o: { p: 2 } }; // BAD, nested pattern parenthesized function f([(z)]) { return z; } // BAD, z parenthesized // Top level ({ p: a }) = { p: 42 }; // BAD, pattern parenthesized ([a]) = [5]; // BAD, pattern parenthesized // Nested [({ p: a }), { x: c }] = [{}, {}]; // BAD, nested pattern parenthesized
Non-array/object patterns in destructuring assignments, outside of declarations, can still be parenthesized:
// Assignments [(b)] = [3]; // OK: parentheses allowed around non-pattern in a non-declaration assignment ({ p: (d) } = {}); // OK: ditto [(parseInt.prop)] = [3]; // OK: parseInt.prop not a pattern, assigns parseInt.prop = 3
Conclusion
These changes shouldn’t much disrupt anyone writing JS. Parentheses around array patterns are unnecessary and are easily removed. For object patterns, instead of parenthesizing the object pattern, parenthesize the whole assignment. No big deal!
// Assignments ([b]) = [3]; // BAD: parentheses around array pattern [b] = [3]; // GOOD ({ p: d }) = { p: 2 }; // BAD: parentheses around object pattern ({ p: d } = { p: 2 }); // GOOD
One step forward for SpiderMonkey standards compliance!