Destructuring is a new ES6 technique for extracting data from JavaScript objects and arrays using a much tighter, terser, and clearer syntax than that provided by ES5. The result is not just the saving of a few bytes of code; destructuring can change the way you code in radically new ways, and the more you use it, the more ways you’ll find of shaping your data and functions in ways that were simply impossible before. This article goes deep into destructuring, providing you with all you need to know about this magical new syntax.
What is Destructuring?
Destructuring is the opposite of data construction. Rather than constructing a new object or array, for example, destructuring takes data from an existing object or array and literally destructs it to extract only the values you’re interested in.
It does this through the use of a pattern that’s used by ES6 to match the values that you want to extract. This pattern mirrors the structure of the data item you’re destrucuturing, with only those parts of the data item that match the pattern, being extracted.
The data item being destructured lies on the right-hand side of an assignment, and can be any combination of objects and arrays, nested arbitrarily deep. The number of variables you use to assign this data to is not limited.
This article presents a deep dive into everything you need to know about destructuring. To get a better feel for how destructuring works, take a look at the code in runnable form in the accompanying Array Destructuring and Object Destructuring fiddles.
Destructuring Arrays
Array destructuring uses an array as a data item, from which you extract values and assign them to one or more variables according to an array pattern, which is used to match the values you need from the array.
This array pattern uses the position of a value to identify the values you want to extract. It must exactly mirror the structure of the array being destructured, with each variable in the array pattern being assigned the value that corresponds to the same position in the array being destructured.
Some examples will help clarify things.
Example Array Patterns
Assign all items in an array to individual variables
// Setup our array
const avengers = ['Tony Stark', 'Steve Rogers', 'Natasha Romanoff'];
// Destructure array into individual variables. The array pattern
// is on the left of the assignment (i.e. the '=') and the array being
// destructured on the right.
const [ironMan, cap, blackWidow] = avengers;
// ironMan = 'Tony Stark'
// cap = 'Steve Rogers'
// blackWidow = 'Natasha Romanoff'
// Output ironMan:
ironMan;
Extract all but the first item
const avengers = ['Tony Stark', 'Steve Rogers', 'Natasha Romanoff'];
// We don't need you, Tony
const [, cap, blackWidow] = avengers;
// ironMan = Error: undefined
// cap = 'Steve Rogers'
// blackWidow = 'Natasha Romanoff'
// Output cap:
cap;
Extract all but the second item
const avengers = ['Tony Stark', 'Steve Rogers', 'Natasha Romanoff'];
// Missing cap
const [ironMan, , blackWidow] = avengers;
// ironMan = 'Tony Stark'
// cap = Error: undefined
// blackWidow = 'Natasha Romanoff'
// Output blackWidow:
blackWidow;
Extract all but the last item
const avengers = ['Tony Stark', 'Steve Rogers', 'Natasha Romanoff'];
// ironMan vs cap
const [ironMan, cap] = avengers;
// ironMan = 'Tony Stark'
// cap = 'Steve Rogers'
// blackWidow = Error: undefined
// Output blackWidow:
ironMan;
Nested Arrays
This concept of pattern matching also works with nested arrays – just ensure the array pattern on the left of the assignment matches the array structure on the right, and again, each variable declared in the array pattern is assigned the value corresponding to its equivalent position in the destructured array. You can nest arrays arbitrarily deep and still destructure from them.
Destructure a nested array
// Destructuring Nested Arrays
const avengers = [
'Natasha Romanoff',
['Tony Stark', 'James Rhodes'],
['Steve Rogers', 'Sam Wilson']
];
// Avengers and their partners
const [blackWidow, [ironMan, warMachine], [cap, falcon]] = avengers;
// blackWidow = 'Natasha Romanoff'
// ironMan = 'Tony Stark'
// warMachine = 'James Rhodes'
// cap = 'Steve Rogers'
// falcon = 'Sam Wilson'
// Output warMachine:
warMachine;
Pluck a single value from a deeply nested array
// Plucking Pepper Potts from a deeply nested Array
const avengers = [
'Natasha Romanoff',
[['Tony Stark', 'Pepper Potts'], 'James Rhodes'],
['Steve Rogers', 'Sam Wilson']
];
// Destructure
const [
, // Skip over 'Natasha Romanoff'
[[
, // Skip over 'Tony Stark'
hera // Pepper Potts, assigned to a variable called 'hera'
]]] = avengers;
// Note: you can also write this like so:
// const [, [[, hera ]]] = avengers;
// Output hera:
hera;
// hera = 'Pepper Potts'
Capture all remaining items with …rest
If you want to capture specific array items, but dump the remaining items in their own array, you can do so by destructuring with the rest operator (…), like so:
// Destructuring with the ...rest operator
const avengers = ['Natasha Romanoff', 'Tony Stark', 'Steve Rogers'];
const [blackWidow, ...theOthers] = avengers;
theOthers;
// blackWidow = 'Natasha Romanoff'
// theOthers = ['Tony Stark', 'Steve Rogers']
// Output theOthers:
theOthers;
Destructuring Objects
Destructuring objects is even more awesome, particularly if you have a complex, deeply nested object whose property values you want to extract. Again, the same rules apply as with arrays – just create an object pattern on the left-hand side of your assignment with the position of the variables you wish to assign to matching the position of values you wish to extract from the object.
With object destructuring, you need to provide the property name whose value you wish to extract, and the name of the variable you want to store the property value in. As with arrays, we need to create an object pattern on the left side of our destructuring assignment that mirrors the object being destructured.
In this case, though, the value that we’re trying to extract is an object property’s value (i.e. we want value
in { prop: value }
). Accordingly, our object pattern must have a variable in exactly the same position as the property value we’re trying to extract.
Simple Examples
Extract a single object property value
To assign the value 'Tony Stark'
to a variable called a
from the property ironMan
in the object { ironMan: 'Tony Stark' }
, we’d do the following:
// Destructure object's property value to an individual variable called 'a'
const { ironMan: a } = { ironMan: 'Tony Stark' };
// Output a:
a; // a = 'Tony Stark '
Extract multiple property values
To extract multiple property values from an object, we just extend the same pattern:
// Setup our object
const avengers = {
ironMan: 'Tony Stark',
cap: 'Steve Rogers',
blackWidow: 'Natasha Romanoff'
};
// Destructure object to individual variables
const {
ironMan: a,
cap: b,
blackWidow: c
} = avengers;
// a = 'Tony Stark '
// b = 'Steve Rogers'
// c ='Natasha Romanoff'
// Output a:
a;
Notice how the destructuring pattern matches exactly the object that we’re destructuring.
Nested Object destructuring
Like nested arrays, we can destructure nested objects of arbitrary depth.
// Setup our object
const avengers = {
blackWidow: 'Natasha Romanoff',
ironManCharacters: {
couple: {
ironMan: 'Tony Stark',
hera: 'Pepper Potts',
},
partner: {
warMachine: 'James Brodie'
}
},
capCharacters: {
cap: 'Steve Rogers',
partner: {
falcon: 'Sam Wilson'
}
}
};
// Destructure object to individual variables
const {
blackWidow: a,
ironManCharacters: {
couple: {
ironMan: b,
hera: c
},
partner: {
warMachine: d
}
},
capCharacters: {
cap: e,
partner: {
falcon: f
}
}
} = avengers;
// a = 'Natasha Romanoff'
// b = 'Tony Stark '
// c = 'Pepper Potts'
// d = 'James Brodie'
// e = 'Steve Rogers'
// f = 'Sam Wilson'
// Output a:
a;
Naming our extracted variables
Of course, naming our variables a
, b
, c
, etc. is horrible, so let’s name them something more meaningful.
Verbose naming
// Setup our object
const avengers = {
ironMan: 'Tony Stark',
cap: 'Steve Rogers',
blackWidow: 'Natasha Romanoff'
};
// Destructure object to individual variables with meaningful names
const {
ironMan: ironMan,
cap: cap,
blackWidow: blackWidow
} = avengers;
// blackWidow = 'Natasha Romanoff'
// ironMan = 'Tony Stark '
// cap = 'Steve Rogers'
// Output blackWidow:
blackWidow;
Better – but we can do better still. { ironMan: ironMan }
seems somewhat ugly and not exactly DRY.
Syntactical naming shortcuts
If you want to assign the value of an object property to a variable of the same name as the property’s, you can simply list the property name once in the destructuring pattern, like so:
// Setup our object
const avenger = {
ironMan: 'Tony Stark'
};
// Destructure object to individual variables with meaningful names
const {
ironMan // equivalent to 'ironMan: ironMan'
} = avenger;
// ironMan = 'Tony Stark '
// Output ironMan:
ironMan;
Because both destructured property name and the name of the variable we’re assigning it to are the same, we only need to list the name once.
Final terse syntax
By reformatting our code slightly, we can make it look terser and cleaner:
// Setup our object
const avengers = {
ironMan: 'Tony Stark',
cap: 'Steve Rogers',
blackWidow: 'Natasha Romanoff'
};
// Destructure object to individual variables with meaningful names
const { ironMan, cap, blackWidow } = avengers;
// Output ironMan:
ironMan;
Extracting a deeply nested property form an object
Things get even more interesting when we want to extract the value of a deeply nested property:
// Setup our object
const avengers = {
blackWidow: 'Natasha Romanoff',
ironManCharacters: {
couple: {
ironMan: 'Tony Stark',
hera: 'Pepper Potts',
},
partner: {
warMachine: 'James Brodie'
}
},
capCharacters: {
cap: 'Steve Rogers',
partner: {
falcon: 'Sam Wilson'
}
}
};
// Destructure a deeply nested object
const { ironManCharacters: { couple } } = avengers;
// couple = {
// ironMan: 'Tony Stark',
// hera: 'Pepper Potts',
// }
// Output couple:
couple;
Wait, WTF?! How do you read this? And how in the world is couple
the variable being defined here?
By breaking this down, we see that the left side of our assignment is mirroring part of the object we’re destructuring:
const avengers = {
ironManCharacters: {
couple: {
ironMan: 'Tony Stark',
hera: 'Pepper Potts',
}
}
};
const {
ironManCharacters: {
couple
}
} = avengers;
// Output couple:
couple;
Just using const { couple } = avengers;
isn’t enough to extract the value of couple
– you have to mirror the position of the property you want to extract as well as the property name. By doing this, you give the JavaScript compiler the information it needs to walk down the object’s properties and extract exactly the value you’re interested in.
Also notice that couple
is using the syntactical shorthand for variable naming, so it’s actually:
const { ironManCharacters: { couple: couple } } = avengers;
So that’s how couple
is the variable that’s being defined, and its value is that of the object property named couple
in the avengers
object.
Destructure to object properties
So far we’ve destructured an object’s values to individual variables, but we can also destructure to another object’s properties:
const avengers = {
blackWidow: 'Natasha Romanoff',
ironManCharacters: {
couple: {
ironMan: 'Tony Stark',
hera: 'Pepper Potts'
}
}
};
const ironManProperties = {
family: {}
};
({
ironManCharacters: {
couple: ironManProperties.family
}
} = avengers);
ironManProperties.family
// ironManProperties.family = {
// ironMan: 'Tony Stark',
// hera: 'Pepper Potts'
// }
// Output ironManProperties.family:
ironManProperties.family;
What we’re doing here is assigning the ironManCharacters.couple
property to the ironManProperties.family
property. There are two things to note here:
- The destructuring assignment is wrapped in parentheses
We have to do this as we’re destructuring to an existing variable (ironManProperties
in this case), rather than declaring a new variable (see below). - We’re still pattern matching
{ ironManCharacters: { couple... } }
matches theironManCharacters
pattern of theavengers
object. This extracts the value ofironManCharacters.couple
from theavengers
object, as you’d expect. But now, as the new objectironManProperties
and its propertyfamily
is placed next tocouple
, it’s the object propertyironManProperties.family
that is assigned this value.
Confusing? Only when you try to explain it! Play around with it in this fiddle, and it’ll soon make sense.
If you’re wondering why you’d ever want to do something like this, take a look at the examples in the next post, where you’ll see how just such a pattern is used to destructure a JSON object from an API call. Then you’ll see how powerful destructuring can be.
Default Values
You can give a variable a default value during destructuring:
// Setup our object
const avengers = {
ironMan: 'Tony Stark',
cap: 'Steve Rogers',
blackWidow: 'Natasha Romanoff'
};
// Destructure using defaults
const { ironMan, cap, blackWidow, theHulk='Bruce Banner' } = avengers;
// ironMan = 'Tony Stark'
// cap = 'Steve Rogers'
// blackWidow = 'Natasha Romanoff'
// theHulk = 'Bruce Banner'
// Output blackWidow:
blackWidow;
Things you can’t do with destructuring
Destructuring assignment without const, let or var
We already touched on this when discussing destructuring to an object property, but it’s worth noting again for clarity:
You can’t directly destructure to a variable that’s already been declared.
That is, you can only destructure to a variable that’s being declared at the same time (i.e. with a const
, let
, or var
).
// Setup our object
const avengers = {
ironMan: 'Tony Stark',
cap: 'Steve Rogers',
blackWidow: 'Natasha Romanoff',
theHulk: 'Bruce Banner'
};
// Valid destructuring
const { ironMan } = avengers;
let { cap } = avengers;
var { blackWidow } = avengers;
// Invalid destructuring
let theHulk;
{ theHulk } = avengers;
// Error
// Output theHulk:
theHulk;
Why can you not destructure to a variable that’s already been declared? Because you cannot start a statement with a curly bracket ( {
), as JavaScript thinks you’re declaring a block.
The solution is to wrap the whole destructuring assignment up in parentheses
How to destructure to an already-declared variable
// Setup our object
const avengers = {
ironMan: 'Tony Stark',
cap: 'Steve Rogers',
blackWidow: 'Natasha Romanoff',
theHulk: 'Bruce Banner'
};
// A valid Hulk
let theHulk;
({ theHulk } = avengers);
// theHulk = 'Bruce Banner'
// Output theHulk:
theHulk;
Now we’re not starting the line with a curly bracket, so JavaScript doesn’t think we’re declaring a block, and so will start destructuring like we want it to.
Directly return a destructured assignment
You cannot directly return a value from a destructured assignment without first creating a variable that you then subsequently return. For example, the following will return the entire ironMan object, and not the value ‘Tony Stark’ that the function name promises:
// Note: this doesn't work!
function getTonyStark(avengers){
return { ironMan: { realName } } = avengers;
// return the avengers object, not the realName value
}
const avengers = {
ironMan: {
realName: 'Tony Stark'
}
};
const tonyStark = getTonyStark(avengers);
// tonyStark = {
// ironMan: {
// realName: 'Tony Stark'
// }
// };
// Output tonyStark:
tonyStark;
To extract a value from a destructured object, you must first assign it to a variable, and then return that variable separately:
// Note: this DOES work!
function getTonyStark(avengers){
const { ironMan: { realName } } = avengers;
return realName;
}
const avengers = {
ironMan: {
realName: 'Tony Stark'
}
};
const tonyStark = getTonyStark(avengers);
// tonyStark = 'Tony Stark'
// Output tonyStark:
tonyStark;
This is annoying, as separating the assignment and return on two lines is ugly and seems unnecessary. Unfortunately, that’s how JavaScript destructuring works – you must first assign the destructured value to a variable and then return it separately.
Summary
That’s quite a deep dive of the key principles of destructuring. Just knowing how things work, though, isn’t enough to show you what you can actually do with this powerful construct.
In the next article, therefore, I’ve put together a list of some advanced destructuring techniques that really show the power of destructuring in ways you may never have thought of.
All posts in this series on destructuring
- Part 1: Destructuring basics with Assembled Avengers
- Part 2: Advanced Destructuring Techniques
- Part 3: Destructuring Recipes
Fiddles for this article
Destructuring Articles around the Web
If you still want more, check out the following:
Juan Jovi
I like the writeup, but would like to point out that currently there are some performance hits to array destructuring. So they are bad for tight loops.
In my test, destructuring was about 24x times slower
console.time('destructuring')
for(var i=0; i<1e5; i++) { var [x,y] = [i, 2*i] }
console.timeEnd('destructuring')
// was ~ 40ms for me on chrome 51
console.time('destructuring')
for(var i=0; i<1e5; i++) { var x = i; var y = 2*i }
console.timeEnd('destructuring')
// was about 1.7 ms on chrome 51
Mike Evans
Thanks Juan, I wasn’t aware of that. Hopefully the JavaScript engines will optimize this performance issue soon.
Ivan
amazing. Nice structured and inline editing! Thank you for great reading 🙂
p.s. please add a button – Facebook share
Mike Evans
Thanks Ivan. The Website’s still a bit rough around the edges, but I’ll certainly add the Facebook share button.
Allan Baptista
Nice article.
But please, avoid the suggested:
// ...
return ({
ironMan: {
realName
}
} = avengers) && realName
// ...
… as it will introduce a new variable in the global scope.
Mike Evans
Yes, you’re right, Allan. Hadn’t even noticed that! I’ve removed the offending code. For those wondering what this referred to, in the section “Directly return a destructured argument”, I’d written a bit of code that returned a destructured argument in a much more terse manner using JavaScript’s short-circuit logical operator
&&
. Specifically, the code was as follows:However, as Allan’s pointed out, because
realName
in thegetTonyStark()
function hasn’t been declared, JavaScript will declare it as a global variable. This can be fixed by declaringrealName
first (e.g.let realName; return ({ ironMan: { realName } } = avengers) && realName;
), but then the code is just as verbose as the example it was meant to be replacing!Ultimately, I decided to remove the example altogether.
Thanks for pointing this out, Allan.
Nenad
Love your website and all the knowledge shared 🙂
keep up the good work
cheers
Shane Wignall
({ theHulk } = avengers);
THANK YOU.