Destructuring Assignment in ECMAScript 6

August 15th, 2013

What is destructuring assignment?

Destructuring assignment allows you to assign the properties of an array or object to variables using syntax that looks similar to array or object literals. This syntax can be extremely terse, while still exhibiting more clarity than the traditional property access.

Without destructuring assignment, you might access the first three items in an array like this:

let first = someArray[0];
let second = someArray[1];
let third = someArray[2];

With destructuring assignment, the equivalent code becomes more concise and read-able:

let [first, second, third] = someArray;

TC39 (the governing committee of ECMAScript) has already reached consensus on destructuring assignment and it is part of the draft ES6 specification. Effectively what this means is that it is now up to the people writing JavaScript engines to start implementing it; SpiderMonkey (Firefox's JS engine) already has support for much of it. Track SpiderMonkey's destructuring (and general ES6) support in this bugzilla ticket.

Destructuring Arrays

We already saw one example of destructuring assignment on an array above. The general form of the syntax is:

[ variable1, variable2, ..., variableN ] = array;

This will just assign variable1 through variableN to the corresponding item in the array. If you want to declare your variables at the same time, you can add a var, let, or const in front of the assignment:

var [ variable1, variable2, ..., variableN ] = array;
let [ variable1, variable2, ..., variableN ] = array;
const [ variable1, variable2, ..., variableN ] = array;

Although variable is a misnomer since you can nest patterns as deep as you would like:

var [foo, [[bar], baz]] = [1, [[2], 3]];
// 1
// 2
// 3

Furthermore, you can skip over items in the array being destructured:

var [,,third] = ["foo", "bar", "baz"];
// "baz"

And you can capture all trailing items in an array with a "rest" pattern:

var [head, ...tail] = [1, 2, 3, 4];
// [2, 3, 4]

When you access items in the array that are out of bounds or don't exist, you get the same result you would by indexing: undefined.

// undefined

var [missing] = [];
// undefined

Destructuring Objects

Destructuring on objects lets you bind variables to different properties of an object. You specify the property being bound, followed by the variable you are binding its value to.

var robotA = { name: "Bender" };
var robotB = { name: "Flexo" };

var { name: nameA } = robotA;
var { name: nameB } = robotB;

// "Bender"
// "Flexo"

There is a helpful syntactical shortcut for when the property and variable names are the same:

var { foo, bar } = { foo: "lorem", bar: "ipsum" };
// "lorem"
// "ipsum"

And just like destructuring on arrays, you can nest and combine further destructuring:

var complicatedObj = {
  arrayProp: [
    { second: "Brannigan" }

var { arrayProp: [first, { second }] } = complicatedObj;

// "Zapp"
// "Brannigan"

When you destructure on properties that are not defined, you get undefined:

var { missing } = {};
// undefined

One potential gotcha you should be aware of is when you are using destructuring on an object to assign variables, but not to declare them (when there is no let, const, or var):

{ blowUp } = { blowUp: 10 };
// Syntax error

This happens because the engine attempts to parse the expression as a block statement (for example, { console } is a valid block statement). The solution is to either wrap the pattern or the whole expression in parenthesis:

({ safe }) = {};
({ andSound } = {});
// No errors

Destructuring Values that are not an Object or Array

When you try to use destructuring on null or undefined, you get a type error:

var [blowUp] = null;
// TypeError: null has no properties

However, you can destructure on other primitive types such as booleans, numbers, and strings, and get undefined:

var [wtf] = NaN;
// undefined

This may come unexpected, but upon further examination it turns out to be simple. When a value is being destructured, it is first converted to an object using the abstract operation ToObject. Most types can be converted to an object, but null and undefined may not be.

Default Values

You can also provide default values for when the property you are destructuring is not defined:

var [missing = true] = [];
// true

var { x = 3 } = {};
// 3

Practical Applications of Destructuring

Function Parameter Definitions

As developers, we can often expose nicer APIs by accepting a single object with multiple properties as a parameter instead of forcing our API consumers to remember the order of many individual parameters. We can use destructuring to avoid repeating this single parameter object whenever we want to reference one of its properties:

function removeBreakpoint({ url, line, column }) {
  // ...

This is a real world snippet of code from Firefox's debugger. We use this all over the place, it is crazy nice.

Configuration Object Parameters

Expanding on the previous example, we can also give default values to the properties of the objects we are destructuring. This is particularly helpful when we have an object that is meant to provide configuration and many of the object's properties already have sensible defaults. For example, jQuery's ajax function takes a configuration object as its second parameter, and could be re-written like this:

jQuery.ajax = function (url, {
  async = true,
  beforeSend = function () {},
  cache = true,
  complete = function () {},
  crossDomain = false,
  global = true,
  // ... more config
}) {
  // ... do stuff

This avoids repeating var foo = || 'default foo'; for each property of the configuration object.

With the ES6 Iteration Protocol

ECMAScript 6 also defines an iteration protocol somewhat similar to the one in Python. When you iterate over Maps (an ES6 addition to the standard library), you get a series of [key, value] pairs. We can destructure this pair to get easy access to both the key and the value:

var map = new Map();
map.set(window, "the global");
map.set(document, "the document");

for (let [key, value] of map) {
  console.log(key + " is " + value);
// "[object Window] is the global"
// "[object HTMLDocument] is the document"

Iterate over only the keys:

for (let [key] of map) {
  // ...

Or iterate over only the values:

for (let [,value] of map) {
  // ...

Multiple Return Values

I've written about this once before. Returning multiple values from a function is a lot more practical when you can destructure the result:

function returnMultipleValues() {
  return [1, 2];
var [foo, bar] = returnMultipleValues();

Alternatively, you can use an object as the container and name the returned values:

function returnMultipleValues() {
  return {
    foo: 1,
    bar: 2
var { foo, bar } = returnMultipleValues();

These both end up much better than holding onto the temporary container:

function returnMultipleValues() {
  return {
    foo: 1,
    bar: 2
var temp = returnMultipleValues();
var foo =;
var bar =;

Importing a Subset of a Module

When importing some module X into your own module Y, it is fairly common that module X exports more functions than you are using. With destructuring, you can be explicit about which parts of module X are being used in module Y:

const { SourceMapConsumer, SourceNode } = require("source-map");

My Talk from Front Trends 2013

June 21st, 2013

At the end of April, I flew to Warsaw, Poland to give a talk about source maps at Front Trends 2013. It is basically the presentation-version of my blog post on the blog. The Front Trends folks just released the recording of the presentation:

"Compiling to JavaScript, and Debugging with Source Maps" on Mozilla Hacks

May 22nd, 2013

I wrote a tutorial titled Compiling to JavaScript, and Debugging with Source Maps for the Mozilla Hacks blog:

This is a tutorial on how to write a compiler which generates JavaScript as its target language, and maintains line and column meta-data in source maps for debugging. Storing line and column coordinates in a source map allows the end-user of the compiler to debug the source code that they wrote, rather than the ugly, generated JavaScript they are not familiar with.

Go read it!

Source Map Specification Discussion Mailing List

February 28th, 2013

If you are interested in discussing the source map specification and any future modifications to it, please join us on the new dev-js-sourcemap mailing list. The Google Groups archive hasn't synced yet, but I am told that it should be synced within a week.

This list exists only for discussing the source map specification, not any of the various tools which use source maps. They each have their own preferred mediums of communication which you should use for tool specific discussions.

Regarding "Dynamic Source Maps"

January 22nd, 2013

Peter van der Zee recently wrote about source maps; in particular, he detailed the ways that he has found them lacking and proposes changes to the source map spec that he believes would improve them to support his use cases.

What Peter wants is to generate and consume source maps at runtime in the browser, and develop without an extra compilation and refresh step. When you are editing and compiling sources inside of the browser, rather than sources on a file server, you can't point to a remote URL where the original source (the source before the compilation step) is located. This is problematic because source URLs are exactly what source maps require, as defined by the spec.

Peter then outlines proposed changes to the source map spec to support his need for "dynamic" source maps. First, adding a new comment pragma //@ sourceMapping=<source map JSON blob>. Second, to include the full original source(s) inside the source map rather than including URLs pointing to the location of the original source(s).

Peter is a very intelligent person, and I have been following his blog for more than two years (you should follow, too), but I believe that he jumped the gun by leaping from his use cases to proposing changes to the source map spec. The one doesn't lead to the other because his use case is already supported by the current specification as-is.

How? Data URLs. Encode the original source(s) as data URLs in the source map's sources list, and encode the whole source map as a data URL in the //@ sourceMappingURL comment pragma appended to the generated source. By doing this, both the whole source map and complete original source(s) are attached directly to the generated source. This accomplishes the same goals that Peter's solution does: you can dynamically generate source maps inside your browser environment (presumably with the mozilla/source-map library) and all of the resources can exist inside your development environment instead of on a remote server.

The benefits to using data URLs are:

If I have overlooked any facet of Peter's use cases that isn't supported by using data URLs, I would love to know in the comments.

« Older Entries

Newer Entries »

Recent Entries

Beyond Source Maps on March 12th, 2014

Memory Tooling in Firefox Developer Tools in 2014 on March 4th, 2014

Hiding Implementation Details with ECMAScript 6 WeakMaps on January 13th, 2014

Re-evaluate Individual Functions in Firefox Developer Tools' Scratchpad on November 22nd, 2013

Testing Source Maps on October 2nd, 2013

Destructuring Assignment in ECMAScript 6 on August 15th, 2013

My Talk from Front Trends 2013 on June 21st, 2013

"Compiling to JavaScript, and Debugging with Source Maps" on Mozilla Hacks on May 22nd, 2013

Source Map Specification Discussion Mailing List on February 28th, 2013

Regarding "Dynamic Source Maps" on January 22nd, 2013

Creative Commons License

Fork me on GitHub