Destructuring Assignment

ES6 provides a way to extract values out of the objects or collections into the separate variables to access them easier in the code. That is often called "value unpacking" or "destructuring".

Basic example

As an example, you can extract a subset of values from the collection using the following format:

let [ <var1>, <var2> ] = <array>

Let's create an array of words and extract the first couple of them into separate variables "first" and "second" like in the code below:

// ES6

let words = [ 'this', 'is', 'hello', 'world', 'example' ];
let [ first, second ] = words;

console.log(first);  // 'this'
console.log(second); // 'is'

As you can see from the example above, you can extract a subset of an array and split it into multiple variables.

Without destructuring your code might look like the following:

// ES5

var words = [ 'this', 'is', 'hello', 'world', 'example' ];
var first = words[0];
var second = words[1];

console.log(first);  // 'this'
console.log(second); // 'is'

Array destructuring

You have already seen some of the array destructuring examples earlier in the section. We enclose variables in square brackets using the following syntax:

let [ <var1>, <var2> ] = <array>

Please note that you can also apply the same destructuring technique to the function call results:

// ES6

function getWords() {
    return [ 'this', 'is', 'hello', 'world', 'example' ];
}

let [ first, second ] = getWords();
console.log(`${first} ${second}`); // 'this is'

In addition to basic unpacking and variable assignment, several other things bring much value and reduce the code.

Value assignment

The destructuring syntax can be used to assign values to variables instead of extracting them. Take a look at the following example:

// ES6

let first, second;

[ first, second ] = [ 'hello', 'world' ];

console.log(first);  // 'hello'
console.log(second); // 'world'

Default values

Another great feature of the array destructuring is default values. There might be cases when the array has no values, and you want to provide some reasonable defaults.

The format of the syntax, in this case, is as follows:

let [ <variable> = <value> ] = <array>

Let's see this feature in action:

// ES6

let words = [ 'hello' ];
let [ first = 'hey', second = 'there' ] = words;

console.log(first);  // 'hello'
console.log(second); // 'there'

The array we got initially does not contain two words. We are trying to extract first two variables from it, and set 'hey' as the default value for the first word, and 'there' as a default for the second one. At the runtime however only second variable stays with the default value.

Default value assignment is a compelling feature that helps you reduce the code for variable initialization and safety checks. Below is how the same code could look like in ES5:

// ES5

var words = ['hello'];

var first = words[0];
if (!first) {
    first = 'hey';
}

var second = words[1];
if (!second) {
    second = 'there'
}

console.log(first);  // 'hello'
console.log(second); // 'there'

Swapping values

Traditionally to swap two variables, developers need a third temporary one to hold the value of either first or second variable.

// ES5

var first = 'world';
var second = 'hello';

var temp = first;
first = second;
second = temp;

console.log(first + ' ' + second); // 'hello world'

With ES6 you can now reduce the code by using destructuring assignment syntax to swap variables in a single line like in the next example:

// ES6

let first = 'world';
let second = 'hello';

[ first, second ] = [ second, first ];

console.log(`${first} ${second}`); // 'hello world'

This feature may be a great time saver when it comes to sorting functions.

Skipping values

We have been using examples that take the beginning of the array so far.
The ES6 does not restrict you to that only scenario; it is also possible skipping values when unpacking or destructuring arrays.

let [ <variable-1>, , , , <variable-X> ] = <array>

You can just put the commas instead of variables like in the example below:

let words = [ 'this', 'is', 'hello', 'world', 'example' ];
let [ first, second, , , last ] = words;

console.log(`${first} ${second} ${last}`); // 'this is example'

Grouping tail values into a single variable

As you see, the ES6 allows you to unpack the head of the array into separate variables. Sometimes you may want to access the tail of the array as a single variable as well.

For this particular case, there's a special syntax that utilizes ES6 "rest" parameters.

let [ <variable1>, <variable2>, ...<restVariable> ] = <array>

We use "rest parameter" to define a variable to hold the tail of the array and below is an example of how to achieve this behavior:

let command = [ 'greet', 'user1', 'user2', 'user3' ];
let [ action, ...users ] = command;

console.log(action); // 'greet'
console.log(users);  // [ 'user1', 'user2', 'user3' ] 

Object destructuring

Besides arrays and collections, you can use destructuring assignment syntax with the object instances as well.

We enclose variables in curly brackets using the following syntax:

let { <var1>, <var2> } = <object>

Unpacking properties

ES6 allows you to extract properties by their names similar to how to unpack arrays.

Let's try to unpack a couple of properties from a user object:

let obj = {
  id: 1,
  username: 'jdoe',
  firstName: 'John',
  lastName: 'Doe'
};

let { id, username } = obj;

console.log(id);        // '1'
console.log(username);  // 'jdoe'

Renaming properties

You can also give destructured property an alias if you want to use it as a variable with a different name.

The syntax, in this case, is going to be as follows:

let { <property> : <alias> } = <object>;

Let's now rewrite our previous example to use custom property names.

let obj = {
  id: 1,
  username: 'jdoe',
  firstName: 'John',
  lastName: 'Doe'
};

let { id: uid, username: login } = obj;

console.log(uid);    // '1'
console.log(login);  // 'jdoe'

We are using "uid" and "login" instead of "id" and "username" properties this time.

Default values

When applying property destructuring to the object properties, you can provide default values for missing properties. That saves time for property checks and reduces coding efforts.

let { <variable> : <value> } = <object>

For example, let's provide a default value for the "id" property and also unpack the property "role" that does not exist for the given object, and set it to be "guest" by default.

let obj = {
  id: 1,
  username: 'jdoe',
  firstName: 'John',
  lastName: 'Doe'
};

let { id = 0, role = 'guest' } = obj;

console.log(id);    // '1'
console.log(role);  // 'guest'

Unpacking methods

You can extract object methods into separate variables and use them as shortcuts:

let { log } = console;
log('hello world');

The example above demonstrates a "console.log" method being extracted into the "log" variable and used separately.

We utilise the following syntax:

let { <method> } = <object>

Next, let's create a custom class and export multiple methods:

// ES6

class MyClass {

  sayHello(message) {
    console.log(`Hello, ${message}`);
  }

  sayBye(message) {
    console.log(`Bye, ${message}`);
  }

}

let myClass = new MyClass();
let { sayHello, sayBye } = myClass;

sayHello('how are you?');  // 'Hello, how are you?'
sayBye('see you soon.');   // 'Bye, see you soon'

Renaming methods

You can also rename destructured methods if needed. The following syntax should be used to give the unpacked method a custom name:

let { <method> : <alias> } = <object>

Let's update the "MyClass" we used earlier and rename "sayHello" and "sayBye" methods to just "hello" and "bye":

let myClass = new MyClass();
let { sayHello: hello, sayBye: bye } = myClass;

hello('how are you?');  // Hello, how are you?
bye('see you soon');    // Bye, see you soon

Using with function parameters

The best scenario for using destructuring with objects and functions is default parameter values and options.

First, let's reproduce the most common use case for the "options" parameter passed to a function or object member:

// ES5

function showDialog(options) {
  options = options || {};
  var message = options.message || 'Unknown message';
  var size = options.size || { width: 400, height: 400 };
  var position = options.position || { x: 200, y: 300 };

  console.log('message: ' + message);
  console.log('size: ' + size.width + ':' + size.height);
  console.log('position: ' + position.x + ':' + position.y);
}

Above is the simplified version of the custom options management that has been very popular for years. We provide a JavaScript object as an "options" parameter, and function does parsing and detecting missing properties to initialize default values if needed.

Depending on the size of the options object there might be many checks just to set the default values for them. Especially if there are nested objects with own properties, like "size" and "position" in our case.

Now, if you call the "showDialog" function with no parameters except the "message" value, the output should be similar to the following one:

showDialog({ 
    message: 'hello world' 
});

// message: hello world
// size: 400:400
// position: 200:300

Next, try to call the same function with a partial set of options, for instance, the "size" settings:

showDialog({
  message: 'hey there',
  size: { width: 200, height: 100 }
});

// message: hey there
// size: 200:100
// position: 200:300

Now you can rewrite the "showDialog" implementation to use destructuring with default values like in the next example:

// ES6

function showDialog({ 
  message = 'Message', 
  size = { width: 400, height: 400 },
  position = { x: 200, y: 300 } }) {

  console.log(`message: ${message}`);
  console.log(`size: ${size.width}:${size.height}`);
  console.log(`position: ${position.x}:${position.y}`);
}

Notice how we use the destructuring assignment syntax to declare a function parameter.

showDialog({
  message: 'hey there',
  size: { width: 200, height: 100 }
});

// message: hey there
// size: 200:100
// position: 200:300

IDE support

Many modern IDEs already provide support for destructuring syntax within function or method parameters. VS Code, for instance, provides auto-completion both for function calls and for nested properties.