Understanding destructuring in Javascript ES6 using visual examples.

One of the popular features that ECMAScript 6 (ES6) introduced is the support of destructuring , a convenient way to extract values from data stored in (possibly nested) objects and arrays.

Although destructuring might seam confusing at first glance but once you understand the simple logic of it advancing to more complicated scenarios would be a relatively easy. Ill try to explain the simple logic behind it with very visual examples and then show some other case uses implying destructuring.

throughout this blog piece i will reference a lot of examples from this very thorough chapter about destructuring from Dr. Axel Rauschmayer’s book ,Exploring ES6: Upgrade to the next version of JavaScript. If you feel the need for extra reference on this subject that would be a great place to start .

You can use destructuring in the following scenarios: when declaring a variable , in assignments and in parameter definitions .

// Variable declarations:
let [x] = ['a'];
const [x] = ['a'];
var [x] = ['a'];

// Assignments:
[x] = ['a'];

// Parameter definitions:
function f([x]) { ··· }
f(['a']);
/* This last example will enforce destructuring of the parameters of the function so you can imagine this is done in the background :
f([x]=['a'] ) { ... }

We divide the destructuring into two sides of the side of a destructuring assignment :

  • Right side aka Destructuring source , the data to be destructured.
  • Left side aka Destructuring target ,the pattern used for destructuring.

So looking at the examples above we would call [x] the destructuring source and [‘a’] the destructuring target. Now that we understand when we can use destructuring lets understand how it works.

Before moving on its important to understand that the logic in which ill explain to you how to predict what the outcome of a destructuring would be is not necissarely how its being executed in the background however it will be easier to understand this porcess through this approach and it will get the same results.

Lets divide this explanation to the two main destructuring cases Array patterns and Object patterns.

Array patterns

Array patterns are very straightforward since they are indexed based. Lets starts with a few examples :

let [x, y] = ['a', 'b'];  // x = 'a'; y = 'b'
let [x, y] = ['a', 'b' ,'g' ,'h'];  // x = 'a'; y = 'b

When destructuring the examples above we go over all the elements on the Destructuring source (left side) and assign to them their index corresponding element in the Destructuring target (right side ).

Object patterns

In object patterns things are a bit differently but it would be easy to understand if you think about it like so :

we match the value of every key in the destructuring source object with the ” result “ of its key being called on the destructuring target object with a dot notation. 

It’s a bit more complex then that but logically this will help you understand this pattern . Lets make sense of the this definition with some examples :

let {boo: x} = { x: 7, boo: 3 }; // x = 7
let {foo: f, bar: b} = { foo: 123, bar: 'abc',temp: 'def' } // f=123, b='abc'

Lets break down whats going on behind the scenes :

The cool things is that the object pattern coerces destructuring sources to objects before accessing properties. That means that it works with primitive values :

let {length : len} = 'abc'; // len = 3
Following the visual examples above you might imagine that the following is being done in the background :
len = ('abc'.length) = > // len = 3

If a part has no match

In both patterns destructuring silently fails if the target mentions a part that doesn’t exist in the source : the interior target element is matched against undefined. If the interior is a variable that means that the variable is set to undefined:

1) let [x] = []; // x = undefined
2) let [z,y] = []; // z = undefined y= undefined
3) let {prop:y} = {}; // y = undefined
Following the visual examples above you might imagine that the following is being done in the background :
1) [x][0] = [][0] => // x = undefined
2 [z,y][0] = [][0] ,[z,y][1] = [][0] => // z = undefined , y= undefined
3) y = {}.prop => // y = undefined

Now that we understand these two basic patterns lets try and implement our knowledge on this nested example.

let obj = { a: [{ foo: 123, bar: 'abc' }, {}], b: true };
let { a: [{foo: f}] } = obj; // f = 123
Following the visual examples above you might imagine that the following is being done in the background :
1) [{foo: f}]= obj.a  /* Object Pattern
2)
[{foo: f}] = [{ foo: 123, bar: 'abc' }, {}] /* Array Pattern
3) [{foo: f}][0] = [{ foo: 123, bar: 'abc' },{}][0]
4)
{foo: f}={ foo: 123, bar: 'abc' } /* Object Pattern
5) f={ foo: 123, bar: 'abc' }.foo
6) f = 123

Understanding the logic behind these two destructuring patterns will allow you to take a part almost any scenario of destructuring . Lets review a few other scenarios that we can apply these patterns to understand the destructuring results:

Strings and the spread operator

Array destructuring uses an iterator to get to the elements of a source. Therefore, you can array-destructure any value that is iterable. Let’s look at examples of iterable values.

Strings are iterable:

let [x,...y] = 'abc'; // x='a'; y=['b', 'c']
You might imagine that the following is being done in the background:
1) [x,...y][0] = ['a','b','c'][0] => x='a'
2) [
x,...y][1]= ['a','b','c'] => ...y = ['a','b','c'] - ['a','b','c'][0] => y = ["b", "c"]

Default values

  • Each part of a pattern can optionally specify a default value.
  • If the part has no match in the source, destructuring continues with the default value (if one exists) or undefined.
let [x=3, y] = []; // x = 3; y = undefined
You might imagine that the following is being done in the background:
[x=3, y][0] = [][0] => x = undefined however it has a default value , 3 //x = 3
[x=3, y][1] = [][1] => y = undefined //y = 3

You can also use default values in object patterns:

let {foo: x=3, bar: y} = {}; // x = 3; y = undefined

Default values are also used if a part does have a match and that match is undefined:

let [x=1] = [undefined]; // x = 1
let {prop: y=2} = {prop: undefined}; // y = 2
You might imagine that the following is being done in the background:
[x=1][0] = [undefined][0] => 
x = undefined
however it has a default value , 1 => //x = 1
y = {prop: undefined}.prop => y = undefined however y has a default value , 2 => //y = 2

Summary :

By now you should be able to use and understand simple destructring scenarios and a few other edge cases . The key to understand most of the destructring scenarios is to break the problem down one by one using one of the two patterns.

There are a few other cases were we can use destructring one of which i will cover in my next blog piece , using destructring to simulate named parameters.

If you can’t wait or just want to explore deeper i suggest you read Dr. Axel Rauschmayer’s book , Exploring ES6: Upgrade to the next version of JavaScript .