Syntax
let [one, two] = [1, 2];
let {ten, eleven} = {ten: 10, eleven: 11};
// skipping
let [one, two, , four] = [1, 2, 3, 4];
let {ten, eleven, thirteen} = {ten: 10, eleven: 11, twelve: 12, thirteen: 13};
// renaming
let {ten, x: eleven} = {ten: 10, x: 11, twelve: 12};
// unwrapping nested objects
let {wrap: {wrap2: {fifty}}} = {wrap: {wrap2: {fifty: 50}}};
// default values
let [zero = 0, xnull = 0, xfalse = 0] = [undefined, null, false];
let {cats = 0, dogs = 0, firstName = '', lastName = ''} = {dogs: 3, firstName: 'x'};
let {surname: lastName = ''} = {surname: 'false'};
Benefits
1. Conciseness
Before
people.forEach(person => {
processFirstName(person.name.first);
processLastName(person.name.last);
processAge(person.age);
processFull(`${person.name.first} ${person.name.last}`, person.age);
});
Or
people.forEach(person => {
let first = person.name.first;
let last = person.name.last;
let age = person.age;
processFirstName(first);
processLastName(last);
processAge(age);
processFull(`${first} ${last}`, age);
});
After
people.forEach(({name: {first, last}, age}) => {
processFirstName(first);
processLastName(last);
processAge(age);
processFull(`${first} ${last}`, age);
});
2. Explicit function interfaces
Before
// unclear what the parameter `settings` should look like without looking at sample invocations.
function updatePage(settings) {};
After
function updatePage({bgColor, fontColor, fontSize}) {};
This issue is mitigated when type annotations are available.
3. Explicit naming of array items
Before
let htmlText = '<p>The blue fox ate the moon grass.</p>';
let textRegex = /\>([A-Z]\w+) ((?:\w|\s)*)([.!?])?\</;
let lineMatch = htmlText.match(textRegex);
printLarge(lineMatch[1]);
print(lineMatch[2]);
printOrDefault(lineMatch[3], '.');
let htmlText = '<p>The blue fox ate the moon grass.</p>';
let textRegex = /\>([A-Z]\w+) ((?:\w|\s)*)([.!?])?\</;
let lineMatch = htmlText.match(textRegex);
let firstWord = lineMatch[1];
let remainingSentence = lineMatch[2];
let punctuation = lineMatch[3];
printLarge(firstWord);
print(remainingSentence);;
printOrDefault(punctuation, '.');
After
let htmlText = '<p>The blue fox ate the moon grass.</p>';
let textRegex = /\>([A-Z]\w+) ((?:\w|\s)*)([.!?])?\</;
let [, firstWord, remainingSentence, punctuation] = htmlText.match(textRegex);
printLarge(firstWord);
print(remainingSentence);
printOrDefault(punctuation, '.');
With Rest operator
Worth noting, destructuring and rest operator can be used together.
let [first, second, third, ...honorableMentions] = [0, 1, 2, 3, 4, 5, 6, 7]; // see [1]
let {president, vicePresident, ...otherImportantPeople} = {president: 0, vicePresident: 1, depStateSec: 2, depDefenseSec: 3, depTreasurySec: 4} // see [2]
Compatibility:
Works with eslint, clang, and Closure [see below]
[1] Rest operator in array destructuring is not supported by iOS.
[2] Rest operators in object destructuring is supported by iOS 11.1+ and closure compiler with `ECMASCRIPT8` flag.
Closure support
1. Rest operator in object destructuring.
As noted above, this requires the `ECMASCRIPT8` flag.
2. Typed arrays
The below snippet fails to throw a compiler error.
/** @type number */ let one;
[one] = [false];
Similarly, for default values during array deconstruction, the below does not throw an error.
/** @type string */ let zero;
/** @type string */ let xnull;
/** @type string */ let xfalse;
[zero = 0, xnull = 0, xfalse = 0] = [undefined, null, false];
But this is a broader issue with typed arrays in general and not unique to destructuring expressions, e.g. the below also fails to throw an error.
/** @type Array<number> */ let array;
array = [false];
Therefore, I don't think this is an argument against allowing destructuring, but still something worth mentioning.