ES6 Destructuring - Mastering the Arcane Art of JavaScriptmancy for C# Developers

| Comments

The Mastering the Arcane Art of JavaScript-mancy series are my humble attempt at bringing my love for JavaScript to all other C# developers that haven’t yet discovered how awesome this language and its whole ecosystem are. These articles are excerpts of the super duper awesome JavaScript-Mancy book a compendium of all things JavaScript for C# developers.

Last summer we got ECMAScript 6 the most significant version update to JavaScript in a loooong time. It is about time to upgrade your JavaScript wizardry to the next level of awesomeness. Today I bring you ES6 destructuring a beautiful way to extract variables from objects and arrays that you can add to your repertoire today.

ECMAScript 6 Destructuring

ES6 comes with a very handy new feature called destructuring that lets you extract parts of information from within objects and other data structures in a very easy way.

You can experiment with all examples in this article directly within this jsBin example.

Destructuring Objects

In the most simple use case, you can initialize variables from properties of objects using the following syntax:

1
2
let pouch = {coins: 10};
let {coins} = pouch;

This is equivalent to:

1
let coins = pouch.coins;

Using destructuring in a real world example would look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let conan = {
        firstName: 'Conan',
        lastName: 'the barbarian',
        height: 178,
        weight: 90,
        email: '[email protected]',
        toString() {
            return this.firstName;
        }
    };

let javascriptmancyCourse = {
    signUp(person){
      let {firstName, lastName} = conan;
      console.log(`Thank you ${firstName}, ${lastName}!!
You have successfully signed up to our very special JavaScript course!
Welcome and prepare to learn some JavaScript!`);
}};

javascriptmancyCourse.signUp(conan);
// => Thank you Conan, the barbarian!! You have succesfully signed up to
//    our very special JavaScriptmancy course! Welcome and prepare to
//    learn some JavaScript!

You are not limited to using variables that have exactly the same names than the properties within the original object. You can use a slightly more advanced destructuring syntax to map an object property to a different variable:

1
let { lastName:title } = conan;

This is equivalent to:

1
let title = conan.lastName;

So that lastName is the name of the origin property and title is the name of the destination variable. Additionally, if you try to extract a property that doesn’t exist in the source object the newly created variable will be undefined:

1
let {bills=10} = pouch;

In these type of cases you can use default values with destructuring syntax so that, if an object doesn’t have a given property, your variable still gets a default value instead of undefined.

1
2
// let pouch = {coins: 10};
let {bills=10} = pouch;

Using defaults with destructuring like in the example above will ensure that you’ll never be poor and will at least enjoy those illusory 10 bills even when they are not in your pouch.

One thing that you cannot do with destructuring is extract a property into an existing variable. So this here won’t work:

1
2
3
// let pouch = {coins: 10};
let money = 0;
{money} = pouch;

One thing that you can do with destructuring is extract properties that are deep within an object, that is, you are not limited to first level properties:

1
2
3
4
5
6
7
8
9
10
11
let bag = {
    leftPocket: {
        tobaccoPouch: ['pipe', 'tobacco']
    },
    rightPocket: [pouch],
    interior: ['10 pieces of dried meat', 'toilet paper', 'leprechaun']
};
let {leftPocket{tobbacoPouch}} = bag;

console.log(`Let's see what I've got in my tobaccoPouch: ${tobaccoPouch}`);
// => Let's see what I've got in my tobaccoPouch: pipe,tobacco

Again if the property you are trying to extract doesn’t exist you’ll get undefined:

1
2
3
let {leftPocket: {secretPouch}} = bag;
console.log(`Let's see what I've got in my secret pouch: ${secretPouch}`);
// => Let's see what I've got in my secret pouch: undefined

But beware, because if there is a missing property in the object graph on the way to the specific property you want, the destructuring will result in a SyntaxError:

1
2
let {centralPocket: {superSecretPouch}} = bag;
// => SyntaxError: Cannot read property 'superSecretPouch' of undefined

In this previous example the centralPocket property doesn’t exist in the bag object, which means that the JavaScript runtime cannot traverse the object to get to the superSecretPouch and thus you get the SyntaxError as a result.

Destructuring Arrays

You are not limited to using destructuring on objects, you can even destructure arrays. Wiii! Let’s destructure!

1
2
3
let [one, two, three] = ['globin', 'ghoul', 'ghost', 'white walker'];
console.log(`one is ${one}, two is ${two}, three is ${three}`)
// => one is globin, two is ghoul, three is ghost

In this example you see how we extract the first three elements of the array and place them in three distinct variales one, two and three. The fourth element in the array, the scary white walker remains unreferenced by any variable.

Additionally you can jump places within the array:

1
2
3
4
let [firstMonster, , , fourthMonster] =
  ['globin', 'ghoul', 'ghost', 'white walker'];
console.log(`the first monster is ${firstMonster}, the fourth is ${fourthMonster}`)
// => one is globin, two is ghoul, three is ghost

Destructuring arrays comes very handy when you want to get the first element of an array in a very readable and intuitive fashion:

1
2
3
let [first] = ['goblin', 'ghoul', 'ghost', 'white walker'];
console.log(`first is ${first}`)
// => first is goblin

which you can combine with the rest operator like this:

1
2
3
let [first, ...rest] = ['goblin', 'ghoul', 'ghost', 'white walker'];
console.log(`first is ${first} and then go all the rest: ${rest}`)
// => first is goblin and then go all the rest ghoul, ghost, white walker

Unfortunately this trick doesn’t work for the last element of the array because the rest operator (the ...) is greedy and wants to extract all items in the array (thus [...initialOnes, last] wouldn’t work out). But you could be a super crafty fox and do something like this:

1
2
3
4
5
let [last] = Array
    .from(['goblin', 'ghoul', 'ghost', 'white walker'])
    .reverse();
console.log(`last is ${last}`);
// => last is whiteWalker

Beautiful1! Another use case for array destructuring is to swap the values of two variables:

1
2
3
4
5
6
7
console.log(`first is ${first}, last is ${last}`);
// => first is goblin, last is white walker

[first, last] = [last, first];

console.log(`but wait! Now first is ${first}, last is ${last}`)
// => but wait! Now first is white walker, last is goblin

Finally, you can also enjoy the beauty of defaults when performing array destructuring:

1
2
3
4
5
6
7
let [aMonster, anotherMonster, yetAnotherMonster='cucumber']
    = ['globin', 'ghoul'];
console.log(`We've got a monster that is a ${aMonster}, 
another that is ${anotherMonster}, and yet another one 
that is a ${yetAnotherMonster}`);
// => We've got a monster that is a globin, another that
//    is ghoul, and yet another one that is a cucumber

In this example because we try to extract three elements from an array that only has two the yerAnotherMonster variable would get a value of undefined. The use of a default prevents that and ensures that the variable has the value of cucumber.

Destructuring Function Arguments

In Useful Function Patterns: Default Parameters you briefly saw how destructuring can be useful when used within the parameter list of a function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// With destructuring we can unpack the direction from
// the incoming object and use it right away
function castIceCone(mana, {direction}){
    var caster = this || 'God almighty';
    console.log(`${caster} spends ${mana} mana
and casts a terrible ice cone ${direction}`);
}
// => Randalf the Mighty spends 10 mana and 
//    casts a terrible ice cone towards Mordor

let randalf = {
    toString: function(){return 'Randalf the Mighty';},
    castIceCone: castIceCone
};
let options = { direction: 'towards Mordor'}
randalf.castIceCone(10, options);

The principle is the same but the destructuring process happens in a more indirect fashion since one bit is the object being passed as an argument to a method, in this case options:

1
randalf.castIceCone(10, options);

And the other bit is the destructuring syntax with the specific variables as part of the method signature {direction}:

1
function castIceCone(mana, {direction}){

And so, following the same principles of destructuring, it also works with arrays:

1
2
3
4
5
6
7
8
9
10
11
function castMiniIceCone(mana, [target, ...others]){
    var caster = this || 'God almighty';
    console.log(`${caster} spends ${mana} mana
and casts a super teeny tiny ice cone that only reaches
${target} but misses ${others} because it is so tiny and cute`);
}
randalf.castMiniIceCone = castMiniIceCone;
randalf.castMiniIceCone(10, ['giant', 'troll', 'death knight']);
// => Randalf the Mighty spends 10 mana 
//    and casts a super teeny tiny ice cone that only reaches
//    giant but misses troll,death knight because it is so tiny and cute

Additionally, you can use any of the destructuring features you’ve learned in this section for both objects and arrays: defaults, deep object properties, jumping over array items, etc.

Concluding

In this article we dived into destructuring and learned how you can use this new feature to easily extract properties from objects and items from arrays. You discovered that you can use destructuring within the arguments of a function and how to combine destructuring and default values when the property you are trying to extract doesn’t exist within an object or array.

Buy the Book!

Are you a C# Developer interested in learning JavaScript? Then take a look at the JavaScript-mancy book, a complete compendium of JavaScript for C# developers.

a JavaScriptmancy sample cover

Have an great week ahead! :)

More Articles in These Series


  1. The next step would be to create two methods first and last that would encapsulate these two implementation and extend the Array.prototype object. We will soon take a look at that within the OOP section of the book.

Comments