Background

  • ES4’s destructuring assignment proposal
  • The ES4 destructuring assignment discussion page
  • Array destructuring is implemented in the Opera browser starting with Opera 8
  • ES4’s proposal is implemented in SpiderMonkey and Rhino

Goals

  • Allow for destructuring of arrays and objects using syntax that mirrors array and object initialisers
  • Destructuring patterns can appear in assignment expressions but also in the various binding forms
  • Create optimizable idioms such as “group assignment”, e.g. [a, b] = [b, a] to swap two variables

Syntax

Pattern ::= "{" (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:

  • in a plain assignment expression
  • in a variable initializer in a var, let or const definition
  • in the first expression in the head of a for-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:

  • in a catch clause head: try {...} catch ({type, message, filename, lineNumber}) {...}
  • in a formal parameter list: function foo([first, second], third) {...}, callable via foo([1, 2], 3)
    • ES1-3 allow duplicate formal parameter names

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

Semantics

Assignments

The meaning of the assignment expression P = E where P is a Pattern and E is an AssignmentExpression is:

  1. Evaluate E yielding a value V
  2. Assign V to a fresh temporary T
  3. If P is an array pattern then
    1. Taking each element L at the top level of P in order, with index I of L in P
      1. If L is an LValue N then
        1. Perform N = T[I]
      2. Else L is a Pattern P’
        1. Destructure P’ and T[I] according to this algorithm (from step 2)
  4. If P is an object pattern then
    1. Taking each field F with name Q at the top level of P in order
      1. If there is no “:” or right-hand side in F
        1. Perform Q = T.Q
      2. If the right-hand-side of F is an LValue N
        1. Perform N = T.Q
      3. Else the right-hand-side of F is a Pattern P’
        1. Destructure P’ and T.Q according to this algorithm (from step 2)
  5. Return T

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.

Variable Definitions

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.

For Loops

Catch Heads

Function Parameters

Examples

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:41Lars 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

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

The idea of {x: a, y: b, ...: c} record extension and row capture for structuring and destructuring comes to mind, but it is a bit of a different operation on objects when structuring (enumerable? own? etc.).

Brendan Eich 2009/07/30 22:25

 
harmony/destructuring.txt · Last modified: 2010/01/30 17:57 by brendan
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki