ES2015 and Destructuring Assignment

This blog is a simple one - I just wanted to write a bit about a neat technique in ES2015 for declaring and assigning variables that I was heretofore unfamiliar with: destructuring assignment.

Recently, while reading a blog post on implementing drag and drop functionality in Ember.js with HTML5, I noticed a method of variable declaration in JavaScript that was new to me. In the code for a specific component, I saw this:

1
2
3
import Ember from 'ember';

var { set } = Ember;

The first line is one I’ve seen a lot - it assigns the actual Ember.js library in the form of an object to an ‘Ember’ variable, giving us easy access to it in the subsequent code. This object consists of a large number of properties, each of which is a method from the Ember.js library. The next line was a bit more puzzling to me. Why was the variable name wrapped in curly braces like that?

So, I did what programmers do best, and soon found myself on the relevant part of MDN’s JavaScript section. Here’s what I learned.

Destructuring assignment

A new feature in ES2015, destructuring assignment syntax allows us to “extract data from arrays or objects into distinct variables”. What this means, is that given an array or object, we can declare a variable and immediately assign it specific elements or properties as a value. Here’s a simple example (we’ll start with arrays and then move to objects):

1
2
3
4
var array = [1,2,3,4]
var [first, second] = array;
console.log(first); // 1
console.log(second); // 2

As you can see, using destructuring assignment in line 2, we declare first and second variables, and assign them values corresponding to the same elements in the array variable. In this case, the first two elements are used, and the rest are automatically skipped. But we could skip the first element too, like this:

1
2
3
4
var array = [1,2,3,4]
var [, second] = array;
console.log(first); // We get an error, telling us that 'first' is undefined.
console.log(second); // 2

We can also assign default values, in case the data extracted from an array is undefined, like the following:

1
2
3
4
5
var first, second;

[first=6, second=7] = [1];
console.log(first); // 1
console.log(second); // 7

Object destructuring

Just like with the examples of arrays above, we can use destructuring assignment on objects too, assigning specific object properties as values to the newly declared variables. Take the following example, where the outfit() function returns an object:

1
2
3
4
5
function outfit(){
  return {pants: "blue", shirt: "red", hat: "yello"};
}

var {pants} = outfit(); // pants = "blue"

As was the case with elements of arrays, we use destructuring assignment to assign a specific property of an object as the variable’s value - in this case, picking out the pants propety so the newly declared variable has a value of the string of ‘blue’. We can also use this assign properties to variables with different names, using the following syntax:

1
2
3
4
5
function outfit(){
  return {pants: "blue", shirt: "red", hat: "yello"};
}

var {pants: trousers} = outfit(); // trousers = "blue"

Here, the new trousers variable has the same value as the pants property.

Back to the initial example

Okay, so now that we understand destructuring assignment a little better, let’s look at that original bit of code from the blog above:

1
2
3
import Ember from 'ember';

var { set } = Ember;

So, clearly we’re looking for a property called ‘set’ that exists in the variable Ember. If you recall from above, the first line of code created the variable Ember, which is an object whose properties are the set of functions comprising the Ember.js library. Looking into the Ember.js library, we find a place where a set function is defined, and above it the following note:

1
2
3
4
5
6
7
8
9
  /**
    Sets the value of a property on an object, respecting computed properties
    and notifying observers and other listeners of the change. If the
    property is not defined but the object implements the `setUnknownProperty`
    method then that will be invoked as well.

    ...

  */

By using destructuring assignment, we assigned the set variable the value of the set function property from this object, which (we can see from the description) lets us change the values on an object in a way that will trigger any observers, listeners, or computed properties, meaning that changes will be reflected in the view. So, basically it’s equivalent of calling Ember.set().

Why do this? Afterall, it’s not much more effort to simply invoke the function with Ember.set(). There are a couple of reasons to use destructuring assignment in general, but in this case I think the answer lies in ensuring the code is resilient. Take a look at the rest of the code from the controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
export default Ember.Component.extend({
classNames        : [ 'draggableDropzone' ],
classNameBindings : [ 'dragClass' ],
dragClass         : 'deactivated',

dragLeave(event) {
  event.preventDefault();
  set(this, 'dragClass', 'deactivated');
},

dragOver(event) {
  event.preventDefault();
  set(this, 'dragClass', 'activated');
},

drop(event) {
  var data = event.dataTransfer.getData('text/data');
  this.sendAction('dropped', data);

  set(this, 'dragClass', 'deactivated');
}
});

As you can see, set is called in three different locations. If the set function in the Ember.js library were renamed or deprecated at any point, then code in this example might break. If we were calling Ember.set() instead of using destructuring assignments and then calling set() each time, we would then have to fix each instance of Ember.set(). As it stands, we would only have to change the initial variable assignment. Let’s say the set function in the Ember.js library was renamed assign, we could simply change things to:

1
2
3
import Ember from 'ember';

var { assign: set } = Ember;

As we learned above, this extracts the hypothetical assign function from the Ember.js library, and assigns it to a new set variable. The existing set variables would still work through the rest of the code, saving us having to fix each case.

Conclusion

All in all, destructuring assignments is a cool bit of syntactic sugar that can help make your code simpler and more resilient. In particular, I see use cases with nested arrays, or when you want to declare a variable with a selection of some of object’s properties where this could save several lines of code by not having to declare each variable individually. I’ve only covered part of the functionality here, so to learn more see the full MDN documentation here.