Object destructuring is a useful JavaScript (ECMAScript 6) feature to extract properties from objects and bind them to variables. As we saw in the ES6 Object Destructuring tutorial, object destructuring can extract multiple properties in one statement, access properties from nested objects, and can set a default value if the property does not exist. In today’s follow-up, we will be taking a look at mixed (Object and Array) destructuring, as well as how to destructure function and method arguments.
What is Mixed Destructuring in JavaScript?
As alluded to in the intro, mixed destructuring involves the declaring and setting of variables from an object that contains attributes of Arrays or from an Array of Objects. To illustrate, here’s a <b>Person Object</b> that contains nested elements of both Objects and Arrays:
const person = { name: 'Rob', location: { city: 'Ottawa', country: 'Canada', phoneNumbers: ['555-1234', '555-2345'], }, };
We can then employ ES6 Destructuring to assign all object properties to individual variables by replicating the object structure:
const { name, location: { city, country, phoneNums: [phone1, phone2], }, } = person; // Now we can reference each variable without having to use dot (.) attribute accessors. // Outputs "I am Rob from Ottawa, Canada. You can reach me at 555-1234 or 555-2345." console.log( `I am ${name} from ${city}, ${country}. You can reach me at ${phone1} or ${phone2}.` );
We can also use ES6 Destructuring to assign Array elements to individual variables, as long as we know the array’s length before-hand. Here is an example that shows how it is done:
const dataArray = [ { data: 1 }, { data: 2 }, { data: 3 } ]; const [ { data: val0 }, { data: val1 }, { data: val2 } ] = dataArray; //Outputs "1, 2, 3" console.log(`${val0}, ${val1}, ${val2}`);
The trick is to replace the element values with variables in which to store them.
Read: A guide to Working with CSS Variables
Destructuring Object Function Arguments in JavaScript
Objects passed to functions can be destructured within the function signature where function arguments are defined. This time, we would replicate the object structure in the arguments list:
//Programmer Object containing nested elements const programmer = { name: 'George', age: 29, skills: { languages: 'JavaScript, HTML, CSS', databases: 'MySQL, MS SQL, MongoDB', }, }; //The programmer object is destructured within the parameters of the function that is passed in const displayEmployeeInfo = ({ name, age, skills: { languages, databases } }) => { console.log( `The employee name is ${name} and his age is ${age}. He knows the following languages - ${languages} and is familiar with the databases - ${databases}.` ); } //Invoke the displayEmployeeInfo() function with the programmer object //Output: The employee name is George and his age is 29. He knows the following //languages - JavaScript, HTML, CSS and is familiar with the databases - MySQL, //MS SQL, MongoDB displayEmployeeInfo(programmer);
Destructured Parameters and Default Values
Like any function parameters, we can assign default values to our local variables right in the function signature. Here is the displayEmployeeInfo() function again with default values assigned to the skills, languages and database variables:
//Programmer Object without skills const programmer = { name: 'George', age: 29, skills: { languages: 'JavaScript, HTML, CSS' } }; const displayEmployeeInfo = ({ name, age, skills: { languages = 'none', databases = 'none' } }) => { console.log( `The employee name is ${name} and his age is ${age}. He knows the following languages - ${languages} and is familiar with the databases - ${databases}.` ); } //Invoke the displayEmployeeInfo() function with the programmer object //Output: The employee name is George and his age is 29. He knows the following //languages - JavaScript, HTML, CSS and is familiar with the databases - none displayEmployeeInfo(programmer);
Making Destructured Parameters Optional
Note that, even though we have specified default values for some of the variables, if we were to call the displayEmployeeInfo() function with no arguments we would get an error because destructured parameters are always required.
const displayEmployeeInfo = ({ name, age, skills: { languages = 'none', databases = 'none' } }) => { console.log( `The employee name is ${name} and his age is ${age}. He knows the following languages - ${languages} and is familiar with the databases - ${databases}.` ); } //Invoking the displayEmployeeInfo() function with no arguments //throws the Error "TypeError: (destructured parameter) is undefined" displayEmployeeInfo();
The key to avoiding the above error is to assign a fallback object literal for all higher-level objects, including the programmer object and the nested skills object.
const displayEmployeeInfo = ({ name = "John Doe", age = "unknown", skills: { languages = 'none', databases = 'none' } = {} } = {}) => { document.write( `The employee name is ${name} and his age is ${age}. He knows the following languages - ${languages} and is familiar with the databases - ${databases}.` ); } //Invoking the function displayEmployeeInfo with the programmer object //Output: The employee name is John Doe and his age is unknown. He knows the following //languages - none and is familiar with the databases - none displayEmployeeInfo();
Final Thoughts on Destructuring Mixed Objects and Function Arguments in ES6
In this web development tutorial, we explored a couple of the more advanced applications of ES6 destructuring syntax. As powerful as destructuring is on its own, they are capable of streamlining your code even further when combined with other ES6 features such Spread Syntax and Rest Parameters.
You will find a demo of today’s code examples on codepen.io. There, you can look over the code and observer the output produced.