JavaScript Shinies: Destructuring

A new series of JS features that are recently here or coming soon, but we start with some stuff that has been here for ages. Nevertheless, it’s often poorly understood and deserves a good look over.

The core principle of destructuring in JavaScript is unpacking elements and objects to get at the gooey inner centre more easily and quickly. Destructuring isn’t all that clear to a lot of people, so we can make an example easily.

Simple Object Destructuring

displayFunction(user) {
const username = user.name;
const messages = user.messages;
// whatever we want to do with it}

But there’s an easier way if we use destructuring.

const { name, messages } = user;

This is not the ideal. We don’t actually need the user object itself here, it’s a temporary variable with no value after we pull the guts out of it. So we can omit it entirely and destructure right in the function definition.

displayFunction({ name, messages }) {

More Complex Object Destructuring

Destructuring allows us to get properties from nested objects as well. The key is that you have to match the format so that destructuring works. As an interesting aside, what helped me most to learn this stuff was actually working with Elixir, in which this is the only way variables are assigned. You learn fast that way.

So assuming our object doesn’t contain a property called name but actually has a details.userName we would do the following instead.

displayFunction({ details: {userName}, messages })

Though it looks like the details property is going to be available, actually only userName and messages will be exposed here. The details: just forms a pattern to match against.

Renaming Object Keys

displayFunction({ details, messageTransactionLog: messages })

From this we get out our details object and a messages array. Groovy.

We can also use any of these in combination.

displayFunction({ details: {displayName: userName}, messageLog: messages })

This will give us only userName and messages. They’ve been named as we want them, and pulled from nested objects.

There is some ambiguity to clear up here - cat: dog

The syntax here is relatively ambiguous, cat is going to have a different value based on the structure. Potentially it could be that dog is an alias of the property cat on the object. But it could also be that the cat is a property of the object, and it’s assigning the object as a dog.

Bit confusing, so let’s clarify that:

const user = {
details: {
name: "Susan"
}
}
// const { details } = user;
console.log(details); // the object at user.details
// const { details: name } = user;
console.log(details); // undefined
console.log(name); // the object at user.details
// const { details: { name } } = user;
console.log(details); // undefined;
console.log(name); // "Susan"

Providing a default

The least obvious syntax for this is going to be our example above with the aliasing and nested object, so we’ll show that with defaults.

displayFunction({ details: {displayName: userName = "Anon"}, messageLog: messages = [] })

The main benefit of this is that it means anything consuming your properties can now be reliably sure of getting at least the right type, and not getting some sort of error on looping over the messages, for example.

Use In Practice — Filter Example

var adultUsers = users.filter(function(user){
if(user.age >= 18) {
return true;
}
return false;
});

This can be refactored in a bunch of ways, not least that you could directly return user.age >= 18 as the boolean, but why not go a bunch better and make it a single line?

const adultUsers = users.filter(({age}) => age >= 18);

This uses destructuring and arrow functions to greatly simplify our code.

Destructuring to Create an Object

const details = { name: "Susan" };
const status = "Active";
const user = { details, status };

This is exactly the same as { details: details, status: status } and will create an object of

{
details: {
name: "Susan"
},
status: "Active"
}

This, by the way, is one reason that aliasing properties is so useful. You can then destructure them directly into a new object. It’s relatively common for people to destructure into React’s setState function, for example. Another place it’s often used is in the definitions of CommonJS modules.

module.exports = { MyClass, someFunction};

Destructuring Arrays

const userArray = ["Steve", "Aoki", "Active", "s.aoki@example.com"];const [firstName, lastName, status, emailAddress] = userArray;

Array access isn’t as reliable as objects, so you can firm things up by skipping elements [firstName, lastName, , emailAddress] or by providing default values using the syntax [firstName, lastName, status="Active"].

A good example of this is the use of the new Object.entries function, which loops over key value pairs in an object, returning an array for each. Because you can destructure the array you can loop over the array returned, and simplify the code like this.

Object.entries(user).map(([key, value]) => {

Though more than a little wordy this will let you loop over the key and value pairs of an object. It’s not the only way, of course, there’s also for .. in and other approaches, though this is my preference.

Conclusion

The techniques demonstrated here are used extensively through modern JavaScript frameworks, especially idiomatic React. Knowing how they work and what they do can make it much easier to write better React, and much easier to read it. The same applies to Vue, which routinely destructures properties off event and state objects.

Senior Web Developer based in Bangkok, Thailand. Javascript, Web and Blockchain Developer.