JavaScript – Destructuring Assignment

Spidermonkey 1.7 implements a kind of compound assignment known
as destructuring assignment. (You may have seen
destructuring assignment before, in Python or Ruby, for example.) In a
destructuring assignment, the value on the right-hand side of the
equals sign is an array or object (a “structured” value) and the
left-hand side specifies one or more variable names using a syntax
that mimics array and object literal syntax. When a destructuring
assignment occurs, one or more values are extracted (“destructured”)
from the value on the right and stored into the variables named on the
left. In addition to its use with the regular assignment operator,
destructuring assignment can also be used when initializing newly
declared variables with var and
let.

Destructuring assignment is simple and powerful when working
with arrays, and is particularly useful with functions that return
arrays of values. It can become confusing and complex when used with
objects and nested objects, however. Examples demonstrating both
simple and complex uses follow.

Here are simple destructuring assignments using arrays of
values:

let [x,y] = [1,2];    // Same as let x=1, y=2
[x,y] = [x+1,y+1];    // Same as x = x + 1, y = y+1
[x,y] = [y,x];        // Swap the value of the two variables
console.log([x,y]);   // Prints [3,2]

Notice how destructuring assignment makes it easy to work with
functions that return arrays of values:

// Convert [x,y] coordinates to [r,theta] polar coordinates
function polar(x,y) {
    return [Math.sqrt(x*x+y*y), Math.atan2(y,x)];
}
// Convert polar to Cartesian coordinates
function cartesian(r,theta) {
    return [r*Math.cos(theta), r*Math.sin(theta)];
}

let [r,theta] = polar(1.0, 1.0);  // r=Math.sqrt(2), theta=Math.PI/4
let [x,y] = cartesian(r,theta);   // x=1.0, y=1.0

The number of variables on the left of a destructuring
assignment does not have to match the number of array elements on the
right. Extra variables on the left are set to undefined, and extra values on the right are
ignored. The list of variables on the left can include extra commas to
skip certain values on the right:

let [x,y] = [1];     // x = 1, y = undefined
[x,y] = [1,2,3];     // x = 1, y = 2
[,x,,y] = [1,2,3,4]; // x = 2, y = 4

There is no syntax to assign all unused or remaining values (as
an array) to a variable. In the second line of code above, for
example, there is no way to assign [2,3] to y.

The value of a destructuring assignment is the complete data
structure on the right-hand side, not the individual values that are
extracted from it. Thus, it is possible to “chain” assignments like
this:

let first, second, all;
all = [first,second] = [1,2,3,4]; // first=1, second=2, all=[1,2,3,4]

Destructuring assignment can even be used with nested arrays. In
this case, the left-hand side of the assignment should look like a
nested array literal:

let [one, [twoA, twoB]] = [1, [2,2.5], 3]; // one=1, twoA=2, twoB=2.5

Destructuring assignment can also be performed when the
right-hand side is an object value. In this case, the left-hand side
of the assignment looks something like an object literal: a
comma-separated and brace delimited list of property name and variable
name pairs. The name to the left of each colon is a property name, and
the name to the right of each colon is a variable name. Each named
property is looked up in the object on the right-hand side of the
assignment, and its value (or undefined) is assigned to the corresponding
variable. This type of destructuring assignment can get confusing,
especially because it is often tempting to use the same identifier for
both property and variable name. In the example below, be sure that
you understand that r, g, and b
are property names and red,
green, and blue are variable names:

let transparent = {r:0.0, g:0.0, b:0.0, a:1.0}; // A RGBA color
let {r:red, g:green, b:blue} = transparent;     // red=0.0,green=0.0,blue=0.0

The next example copies global functions of the Math object into variables, which might
simplify code that does a lot of trigonometry:

// Same as let sin=Math.sin, cos=Math.cos, tan=Math.tan
let {sin:sin, cos:cos, tan:tan} = Math;

Just as destructuring assignment can be used with nested arrays,
it can be used with nested objects. In fact, the two syntaxes can be
combined to describe arbitrary data structures. For example:

// A nested data structure: an object that contains an array of objects
let data = {
    name: "destructuring assignment",
    type: "extension",
    impl: [{engine: "spidermonkey", version: 1.7},
           {engine: "rhino", version: 1.7}]
};

// Use destructuring assignment to extract four values from the data structure
let ({name:feature, impl: [{engine:impl1, version:v1},{engine:impl2}]} = data) {
    console.log(feature);  // Prints "destructuring assignment"
    console.log(impl1);    // Prints "spidermonkey"
    console.log(v1);       // Prints 1.7
    console.log(impl2);    // Prints "rhino"
}

Note that nested destructuring assignments like this may make
your code harder to read rather than simplifying it. There is an
interesting regularity that can help you to make sense of the complex
cases, however. Think first about a regular (single-value) assignment.
After the assignment is done, you can take the variable name from the
left-hand side of the assignment and use it as an expression in your
code, where it will evaluate to whatever value you assigned it. In
destructuring assignment, we’ve said that the left-hand side uses a
syntax like array literal syntax or like object literal syntax. But
notice that after the destructuring assignment is done, the code that
looks like an array literal or object literal from the left-hand side
will actually work as a valid array literal or object literal
elsewhere in your code: all the necessary variables have been defined
so that you can cut-and-paste the text on the left of the equals sign
and use it as an array or object value in your code.

Comments are closed.