Destructuring Assignment
Destructuring assignment is syntax in JavaScript that allows you to unpack values from arrays or properties from objects, and save them into variables. It is a very handy feature because we can extract data directly from arrays and objects to save into variables, all on a single line of code. It is powerful because it enables us to extract multiple array elements or object properties in the same expression.
Array Destructuring
Array destructuring allows us to extract multiple array elements and save them into variables. In ES5, we do this by defining each variable with its array value, one variable at a time. This makes the code lengthy and increases the time required to write it.
In ES6, to destructure an array, we simply create an array containing the variable to assign data into, and set it equal to the data array being destructured. The values in the array are unpacked and assigned to the variables in the left-hand side array from left to right, one variable per array value. An example of basic array destructuring is shown in the following code:
let names = [ 'John', 'Michael' ]; let [ name1, name2 ] = names; console.log( name1 ); // Expected output: 'John' console.log( name2 ); // Expected output: 'Michael'
Snippet 1.37: Basic array destructuring
As can be seen in this example, we have an array of names and we want to destructure it into two variables, name1
and name2
. We simply surround the variables name1
and name2
with brackets and set that expression equal to the data array names, and then JavaScript will destructure the names
array, saving data into each of the variables.
The data is destructured from the input array into the variables from left to right, in the order of array items. The first index variable will always be assigned the first index array item. This leads to the question, what do we do if we have more array items than variables? If there are more array items than variables, then the remaining array items will be discarded and will not be destructured into variables. The destructuring is a one to one mapping in array order.
What about if there are more variables than array items? If we attempt to destructure an array into an array that contains more variables than the total number of array elements in the data array, some of the variables will be set to undefined. The array is destructured from left to right. Accessing a non-existent element in a JavaScript array results in an undefined value to be returned. This undefined value is saved to the leftover variables in the variable array. An example of this is shown in the following code:
let names = [ 'John', 'Michael' ]; let [ name1 ] = names let [ name2, name3, name4 ] = names; console.log( name1 ); // Expected output: 'John' console.log( name2 ); // Expected output: 'John' console.log( name3 ); // Expected output: 'Michael' console.log( name4 ); // Expected output: undefined
Snippet 1.38: Array destructuring with mismatched variable and array items
Note
We must be careful when destructuring arrays to make sure that we don't unintentionally assume that a variable will contain a value. The value of the variable could be set to undefined if the array is not long enough.
ES6 array destructuring allows for skipping array elements. If we have an array of values and we only care about the first and third values, we can still destructure the array. To ignore a value, simply omit the variable identifier for that array index in the left-hand side of the expression. This syntax can be used to ignore a single item, multiple items, or even all the items in an array. Two examples of this are shown in the following snippet:
let names = [ 'John', 'Michael', 'Jessica', 'Susan' ]; let [ name1,, name3 ] = names; // Note the missing variable name for the second array item let [ ,,, ] = names; // Ignores all items in the array console.log( name1 ); // Expected output: 'John' console.log( name3 ); // Expected output: 'Jessica'
Snippet 1.39: Array destructuring with skipped values
Another very useful feature of array destructuring is the ability to set default values for variables that are created with destructuring. When we want to add a default value, we simply need to set the variable equal to the desired default value in the left-hand side of the destructuring expression. If what we are destructuring does not contain an index to assign to the variable, then the default value will be used instead. An example of this is shown in the following code:
let [ a = 1, b = 2, c = 3 ] = [ 'cat', null ]; console.log( a ); // Expected output: 'cat' console.log( b ); // Expected output: null console.log( c ); // Expected output: 3
Snippet 1.40: Array destructuring with skipped values
Finally, array destructuring can be used to easily swap values of variables. If we wish to swap the value of two variables, we can simply destructure an array into the reversed array. We can create an array containing the variables we want to reverse and set it equal to the same array, but with the variable order changed. This will cause the references to be swapped. This is shown in the following code:
let a = 10; let b = 5; [ a, b ] = [ b, a ]; console.log( a ); // Expected output: 5 console.log( b ); // Expected output: 10
Snippet 1.41: Array destructuring with skipped values
Exercise 8: Array Destructuring
To extract values from an array using array destructuring assignment, perform the following steps:
Create an array with three values,
1
,2
, and3
, and save it into a variable calleddata
.Destructure the array created with a single expression.
Destructure the first array value into a variable called
a
. Skip the second value of the array.Destructure the third value into a variable called
b
. Attempt to destructure a fourth value into a variable calledc
and provide a default value of4
.Log the value of all of the variables.
Code
index.js:
const data = [ 1, 2, 3 ]; const [ a, , b, c = 4 ] = data; console.log( a, b, c );
Snippet 1.42: Array destructuring
Outcome
You have successfully applied an array destructuring assignment to extract values from an array.
In summary, array destructuring allows us to quickly extract values from arrays and save them into variables. Variables are assigned to array values, item by item, from left to right. If the number of variables exceeds the number of array items, then the variables are set to undefined, or the default value if specified. We can skip an array index in the destructuring by leaving a hole in the variables array. Finally, we can use destructuring assignment to quickly swap the values of two or more variables in a single line of code.
Rest and Spread Operators
ES6 also introduces two new operators for arrays called rest and spread. The rest and spread operators are both denoted with three ellipses or periods before an identifier ( ...array1
). The rest operator is used to represent an infinite number of arguments as an array. The spread operator is used to allow an iterable object to be expanded into multiple arguments. To identify which is being used, we must look at the item that the argument is being applied to. If the operator is applied to an iterable object (array, object, and so on), then it is the spread operator. If the operator is applied to function arguments, then it is the rest operator.
Note
In JavaScript, something considered iterable if something (generally values or key/value pairs) can be stepped through one at a time. For example, an array is iterable because the items in the array can be stepped through one at a time. Objects are considered iterable because the key/value pairs can be stepped through one at a time.
The rest operator is used to represent an indefinite number of arguments as an array. When the last parameter of a function is prefixed with the three ellipses, it becomes an array. The array elements are supplied by the actual arguments that are passed into the function, excluding the arguments that already have been given a separate name in the formal declaration of the function. An example of rest destructuring is shown in the following code:
function fn( num1, num2, ...args ) { // Destructures an indefinite number of function parameters into the //array args, excluding the first two arguments passed in. console.log( num1 ); console.log( num2 ); console.log( args ); } fn( 1, 2, 3, 4, 5, 6 ); // Expected output // 1 // 2 // [ 3, 4, 5, 6 ]
Snippet 1.43: Array destructuring with skipped values
Similar to the arguments object of a JavaScript function, the rest operator contains a list of function arguments. However, the rest operator has three distinct differences from the arguments object. As we already know, the arguments object is an array-like object that contains each argument that's passed into the function. The differences are as follows. First, the rest operator contains only the input parameters that have not been given a separate formal declaration in the function expression.
Second, the arguments object is not an instance of an Array object. The rest parameter is an instance of an array, which means that array functions like sort()
, map()
, and forEach()
can be applied to them directly.
Lastly, the arguments object has special functionality that the rest parameter does not have. For example, the caller property exists on the arguments object.
The rest parameter can be destructured similar to how we destructure an array. Instead of putting a single variable name inside before the ellipses, we can replace it with an array of variables we want to fill. The arguments passed into the function will be destructured as expected for an array. This is shown in the following code:
function fn( ...[ n1, n2, n3 ] ) { // Destructures an indefinite number of function parameters into the // array args, which is destructured into 3 variables console.log( n1, n2, n3 ); } fn( 1, 2 ); // Expected output: 1, 2, undefined
Snippet 1.44: Destructured rest operator
The spread operator allows an iterable object such as an array or string to be expanded into multiple arguments (for function calls), array elements (for array literals), or key-value pairs (for object expressions). This essentially means that we can expand an array into arguments for creating another array, object, or calling a function. An example of spread syntax is shown in the following code:
function fn( n1, n2, n3 ) { console.log( n1, n2, n3 ); } const values = [ 1, 2, 3 ]; fn( ...values ); // Expected output: 1, 2, 3
Snippet 1.45: Spread operator
In the preceding example, we created a simple function that takes in three inputs and logs them to the console. We created an array with three values, then called the function using the spread
operator to destructure the array of values into three input parameters for the function.
The rest operator can be used in destructuring objects and arrays. When destructuring an array, if we have more array elements than variables, we can use the rest operator to capture, or catch, all of the additional array elements during destructuring. When using the rest operator, it must be the last parameter in the array destructuring or function arguments list. This is shown in the following code:
const [ n1, n2, n3, ...remaining ] = [ 1, 2, 3, 4, 5, 6 ]; console.log( n1 ); // Expected output: 1
console.log( n2 ); // Expected output: 2 console.log( n3 ); // Expected output: 3 console.log( remaining ); // Expected output: [ 4, 5, 6 ]
Snippet 1.46: Spread operator
In the preceding snippet, we destructured the first three array elements into three variables, n1
, n2
, and n3
. We then captured the remaining array elements with the rest operator and destructured them into the variable that remained.
In summary, the rest and spread operators allow iterable entities to be expanded into many arguments. They are denoted with three ellipses before the identifier name. This allows us to capture arrays of arguments in functions or unused items when destructuring entities. When we use the rest and spread operators, they must be the last arguments that are passed into the expression they are being used in.
Object Destructuring
Object destructuring is used in a very similar way to array destructuring. Object destructuring is used to extract data from an object and assign the values to new variables. In ES6, we can do this in a single JavaScript expression. To destructure an object, we surround the variables we want to destructure with curly braces ({}
), and set that expression equal to the object we are destructuring. A basic example of object destructuring is shown in the following code:
const obj = { firstName: 'Bob', lastName: 'Smith' }; const { firstName, lastName } = obj; console.log( firstName ); // Expected output: 'Bob' console.log( lastName ); // Expected output: 'Smith'
Snippet 1.47: Object destructuring
In the preceding example, we created an object with the keys firstName
and lastName
. We then destructured this object into the variables firstName
and lastName
. Notice that the names of the variables and the object parameters match. This is shown in the following example:
Note
When doing basic object destructuring, the name of the parameter in the object and the name of the variable we are assigning it to must match. If there is no matching parameter for a variable we are trying to destructure, then the variable will be set to undefined.
const obj = { firstName: 'Bob', lastName: 'Smith' }; const { firstName, middleName } = obj; console.log( firstName ); // Expected output: 'Bob' console.log( middleName ); // Expected output: undefined
Snippet 1.48: Object destructuring with no defined key
As we saw, the middleName
key does not exist in the object. When we try to destructure the key and save it into the variable, it is unable to find a value and the variable is set to undefined.
With advanced object destructuring syntax, we can save the key that's extracted into a variable with a different name. This is done by adding a colon and the new variable name after the key name in the destructuring notation. This is shown in the following code:
const obj = { firstName: 'Bob', lastName: 'Smith' }; const { firstName: first, lastName } = obj; console.log( first ); // Expected output: 'Bob' console.log( lastName ); // Expected output: 'Smith'
Snippet 1.49: Object destructuring into new variable
In the preceding example, we could clearly see that we are destructuring the firstname
key from the object and saving it into the new variable, called first. The lastName
key is being destructured normally and is saved into a variable called lastName
.
Much like with array destructuring, we can destructure an object and provide a default value. If a default value is provided and the key we are attempting to destructure does not exist in the object, then the variable will be set to the default value instead of undefined. This is shown in the following code:
const obj = { firstName: 'Bob', lastName: 'Smith' }; const { firstName = 'Samantha', middleName = 'Chris' } = obj; console.log( firstName ); // Expected output: 'Bob' console.log( middleName ); // Expected output: 'Chris'
Snippet 1.50: Object destructuring with default values
In the preceding example, we set the default values for both of the variables we are trying to destructure from the object. The default value for firstName
is specified, but the firstName
key exists in the object. This means that the value stored in the firstName
key is destructured and the default value is ignored. The middleName
key does not exist in the object and we have specified a default value to use when destructuring. Instead of using the undefined value of the firstName
key, the destructuring assignment sets the destructured variable to the default value of Chris
.
When we are providing a default value and assigning the key to a new variable name, we must put the default value assignment after the new variable name. This is shown in the following example:
const obj = { firstName: 'Bob', lastName: 'Smith' }; const { firstName: first = 'Samantha', middleName: middle = 'Chris' } = obj; console.log( first ); // Expected output: 'Bob' console.log( middle); // Expected output: 'Chris'
Snippet 1.51: Object destructuring into new variables with default values
The firstName
key exists. The value of obj.firstName
is saved into the new variable named first
. The middleName
key does not exist. This means that the new variable middle is created and set to the default value of Chris
.
Exercise 9: Object Destructuring
To extract data from an object by using object destructuring concepts, perform the following steps:
Create an object with the fields
f1
,f2
, andf3
. Set the values tov1
,v2
, andv3
, respectively. Save the object into thedata
variable.Destructure this object into variables with a single statement, as follows:
Destructure the
f1
property into a variable namedf1.
Destructure thef2
property into a variable namedfield2.
Destructure the propertyf4
into a variable namedf4
and provide a default value ofv4
.Log the variables that are created.
Code
index.js:
const data = { f1: 'v1', f2: '2', f3: 'v3' }; const { f1, f2: field2, f4 = 'v4' } = data; console.log( f1, field2, f4 );
Snippet 1.52: Object destructuring
Outcome
You have successfully applied object destructuring concepts to extract data from an object.
JavaScript requires special syntax if we declare the variables before the object destructuring expression. We must surround the entire object destructuring expression with parentheses. This syntax is not required for array destructuring. This is shown in the following code:
const obj = { firstName: 'Bob', lastName: 'Smith' }; let firstName, lastName; ( { firstName: first, lastName } = obj ); // Note parentheses around expression console.log( firstName ); // Expected output: 'Bob' console.log( lastName ); // Expected output: 'Smith'
Snippet 1.53: Object destructuring into predefined variables
Note
Make sure that object destructuring done in this way is preceded by a semicolon on the same or previous line. This prevents the JavaScript interpreter from interpreting the parentheses as a function call.
The rest operator can also be used to destructure objects. Since object keys are iterable, we can use the rest operator to catch the remaining keys that were uncaught in the original destructuring expression. This is done similar to arrays. We destructure the keys that we want to capture, and then we can add the rest operator to a variable and catch the remaining key/value pairs that have not been destructured out of the object. This is shown in the following example:
const obj = { firstName: 'Bob', middleName: 'Chris', lastName: 'Smith' }; const { firstName, ...otherNames } = obj; console.log( firstName ); // Expected output: 'Bob' console.log( otherNames ); // Expected output: { middleName: 'Chris', lastName: 'Smith' }
Snippet 1.54: Object destructuring with the rest operator
In summary, object destructuring allows us to quickly extract values from objects and save them into variables. The key name must match the variable name in simple object destructuring, however we can use more advanced syntax to save the key's value into a new object. If a key is not defined in the object, then the variable will be set to false
, that is, unless we provide it with a default value. We can save this into predefined variables, but we must surround the destructuring expression with parentheses. Finally, the rest operator can be used to capture the remaining key value pairs and save them in a new object.
Object and array destructuring support nesting. Nesting destructuring can be a little confusing, but it is a powerful tool because it allows us to condense several lines of destructuring code into a single line.
Exercise 10: Nested Destructuring
To destructure values from an array that's nested inside an object using the concept of nested destructuring, perform the following steps:
Create an object with a property,
arr
, that is, set to an array containing the values1
,2
, and3
. Save the object into thedata
variable.Destructure the second value of the array into a variable by doing the following:
Destructure the
arr
property from the object and save it into a new variable calledv2
, which is the array. Replacev2
with array destructuring.In the array destructuring, skip the first element. Save the second element into a variable called
v2
.Log the variable.
Code
index.js:
const data = { arr: [ 1, 2, 3 ] }; const { arr: [ , v2 ] } = data; console.log( v2 );
Snippet 1.55: Nested array and object destructuring
Outcome
You have successfully destructured values from an array inside an object.
In summary, object and array destructuring was introduced into ES6 to cut down code and allow for the quick creation of variables from objects and arrays. Array destructuring is denoted by setting an array of variables equal to an array of items. Object destructuring is denoted by setting an object of variables equal to an object of key value pairs. Destructuring statements can be nested for even greater effect.
Exercise 11: Implementing Destructuring
You have registered for university courses and need to buy the texts required for the classes. You are building a program to scrape data from the book list and obtain the ISBN numbers for each text book that's required. Use object and array nested destructuring to obtain the ISBN value of the first text of the first book in the courses array. The courses array follows the following format:
[ { title: 'Linear Algebra II', description: 'Advanced linear algebra.', texts: [ { author: 'James Smith', price: 120, ISBN: '912-6-44-578441-0' } ] }, { ... }, { ... } ]
Snippet 1.56: Course array format
To obtain data from complicated array and object nesting by using nested destructuring, perform the following steps:
Save the provided data structure into the
courseCatalogMetadata
variable.Destructure the first array element into a variable called
course
:[ course ] = [ … ]
Replace the
course
variable with object destructuring to save the texts field into a variable calledtextbooks
:[ { texts: textbooks} ] = [ … ]
Replace the
textbooks
variable with array destructuring to get the first element of the texts array and save it into the variable calledtextbook
:[ { texts: [ textbook ] } ] = [ … ]
Replace the
textbook
variable with object destructuring to get theISBN
field and save it into theISBN
variable:[ { texts: [ { ISBN } ] } ] = [ … ]
Log the value of the
ISBN
.
Code
index.js:
const courseCatalogMetadata = [ { title: 'Linear Algebra II', description: 'Advanced linear algebra.', texts: [ { author: 'James Smith', price: 120, ISBN: '912-6-44-578441-0' } ] } ]; const [ course ] = courseCatalogMetadata; const [ { texts: textbooks } ] = courseCatalogMetadata; const [ { texts: [ textbook ] } ] = courseCatalogMetadata; const [ { texts: [ { ISBN } ] } ] = courseCatalogMetadata; console.log( course ); console.log( textbooks ); console.log( textbook ); console.log( ISBN );
Snippet 1.57: Implementing destructuring into code
Outcome
You have successfully obtained data from arrays and objects using destructuring and nested destructuring.
In this section, we discussed destructuring assignment for arrays and objects. We demonstrated how array and object destructuring simplifies code and allows us to quickly extract values from objects and arrays. Destructuring assignment allows us to unpack values from objects and arrays, provide default values, and rename object properties as variables when destructuring. We also introduced two new operators— the rest and spread operators. The rest operator was used to represent an indefinite number of arguments as an array. The spread operator was used to break an iterable object into multiple arguments.