[a, b] = [b, a] to swap two variablesPattern ::= "{" (Field ("," Field)*)* "}"
| "[" ((Pattern | LValue)? ",")* "]"
Field ::= Identifier (":" (Pattern | LValue))?
LValue ::= <any lvalue expression allowed in a normal assignment expression>
A Pattern may appear only on the left-hand-side of “=” in the following contexts:
var, let or const definitionfor-in loop
If preceded by a var, let, or const the Pattern must contain LValues that are all identifiers, else the compiler must throw a SyntaxError.
A Pattern may appear not on the left-hand side of “=” in the following contexts:
catch clause head: try {...} catch ({type, message, filename, lineNumber}) {...}function foo([first, second], third) {...}, callable via foo([1, 2], 3)In these cases all LValues must be identifiers. Thus only assignment expressions may bind other lvalues than fresh identifiers.
“Don’t Repeat Yourself” motivates a shorthand familiar from ML for object patterns: {x, y} in a pattern context is equivalent to {x: x, y: y}.
Note that an object initialiser cannot appear at the start of a statement, so an object destructuring assignment statement { x } = y must be parenthesized either as ({ x } = y) or ({ x }) = y.
Empty patterns are allowed. Rationale: same basis case as for initialisers helps code generators avoid extra checks.
(We don’t understand the statement about Empty patterns. Besides, they aren’t allowed by the above grammar. – MarkM & Tom)
The grammar does produce empty patterns such as in var {} = obj; and [] = returnsArray(). Note the parentheses and Kleene * usage. The rationale is that empty initialisers are allowed, and indeed top-down parsers must parse an empty pattern on the left of assignment as a primary initialiser expression first, only then discover a = token and revise the AST appropriately to make the initialiser be a destructuring pattern.
The further rationale is that code generators should not need to check and avoid emitting a no-op empty destructuring assignment or binding, if it happens that there are no properties or elements to destructure for the particular case the code generator is handling (say, based on database records or JSON). This is a bit thin by itself, but with the first point (symmetry with initialisers) it adds up to a better case for empty patterns.
— Brendan Eich 2010/01/30 17:51
The meaning of the assignment expression P = E where P is a Pattern and E is an AssignmentExpression is:
As can be seen, destructuring assignment is simple syntactic sugar.
Note: In contrast with normal assignment expressions, the locations updated by destructuring assignment are not computed before the value that is to be stored. Destructuring assignment is simple syntactic sugar for a common compute-and-destructure pattern, and true to this pattern it computes the value prior to computing the locations. Apart from with and embedding-specific deep scope chains, there is no difference between prebinding lvalues vs. post-binding them.
The meaning of DefiningKeyword P = E where the DefiningKeyword can be var, let, or const and all LValues in the Pattern P are simple identifiers i1, i2, ..., is DefiningKeyword i1, i2, ... ; P = E.
Swap:
[a, b] = [b, a]
Multiple-value returns:
function f() { return [1, 2] } var a, b; [a, b] = f();
Multiple-value returns, some values are not interesting:
function f() { return [1, 2, 3] } var [a, , b] = f();
Going deeper into the array:
[a,,[b,,[c]]] = f();
Object destructuring:
var { op: a, lhs: b, rhs: c } = getASTNode()
Digging deeper into an object:
var { op: a, lhs: { op: b }, rhs: c } = getASTNode()
Looping across an object:
for ( let [name, value] in obj ) print("Name: " + name + ", Value: " + value);
Looping across values in an object:
for each ( let { name: n, family: { father: f } } in obj ) print("Name: " + n + ", Father: " + f);
Summing the salary fields of all records whose record key begins with N:
for ( let [[k], { "salary": s }] in database ) if (k == "N") sum += s;
Function that destructures its first argument and accepts some optional object arguments:
function f( { "name": n } : Person, ...[ a, b, c ] )
{
}
— Brendan Eich 2009/05/06 01:41 — Lars T Hansen 2007/01/16 07:47
It would be nice if we could get destructuring to work with rest arguments/spread operator.
[x, ...xs] = someList
— Erik Arvidsson 2009/07/30 18:48
Erik: good idea, I will write it up in the rest parameters proposal.
The idea of {x: a, y: b, ...: c} – record extension and row capture – for structuring and destructuring comes to mind, but ES objects are a different animal compared to ML records. For example, which properties of c are copied into the object being initialized by {x: a, y: b, ...: c}? enumerable? own? etc.
— Brendan Eich 2009/07/30 22:25
The shorthand {x, y} for {x: x, y: y} allowed on the left-hand side of assignment should be allowed on the right too, markm suggests.
— Brendan Eich 2009/07/30 22:10
Allowing {x, y} in an object literal expression seems like a great idea. It seems useful since it makes patterns reversible and it also makes common programming patterns shorter:
var obj = (function() {
function foo() {...}
...
return {foo};
})();
— Erik Arvidsson 2010/04/08 03:08
Agreed – see object initialiser shorthand.
— Brendan Eich 2010/05/26 21:57