ECMAScript 2015 (ES6) introduced object destructuring, a powerful syntax that allows you to extract properties from objects and assign them to variables in a single, concise statement. This feature simplifies the process of working with complex data structures by enabling you to map object properties directly to variables using a syntax that mirrors the object's structure.
Consider the following traditional approach for extracting properties from an object:
const user = {
userName: 'Alex',
userAge: 32
};
const userName = user.userName;
const userAge = user.userAge;
console.log(userName); // Alex
console.log(userAge); // 32
With object destructuring, you can achieve the same result more elegantly:
const user = {
userName: 'Alex',
userAge: 32
};
const { userName, userAge } = user;
console.log(userName); // Alex
console.log(userAge); // 32
The destructuring syntax uses curly braces {} on the left-hand side of the assignment. Inside these braces, you specify the property names you want to extract. The JavaScript engine then looks for matching properties in the object on the right-hand side and assigns their values to the corresponding variables.
Variable Renaming
You can also assign the extracted value to a variable with a different name by using a colon (:). This is useful when you need to avoid naming conflicts or prefer more descriptive variable names.
const user = {
userName: 'Alex',
userAge: 32
};
const { userName: displayName, userAge: yearsOld } = user;
console.log(displayName); // Alex
console.log(yearsOld); // 32
Default Values
If you try to destructure a property that does not exist in the source object, the corresponding variable will be assigned undefined. To handle this, you can provide a default value that will be used if the property is missing.
const user = {
userName: 'Alex',
userAge: 32
};
const { userName, role = 'Developer' } = user;
console.log(userName); // Alex
console.log(role); // Developer
Restrictions with null and undefined
Object destructuring internally uses the ToObject() abstract operation. This means it attempts to convert the source value into an object before extracting properties. Consequently, you cannot destructure null or undefined, as they cannot be converted in to objects, which will result in a TypeError.
// This works because numbers are converted to their Number object wrapper
const { toString } = 123;
console.log(toString === Number.prototype.toString); // true
// These will throw a TypeError
const { _ } = null; // TypeError: Cannot destructure property '_' of 'null' or 'undefined'.
const { _ } = undefined; // TypeError: Cannot destructure property '_' of 'null' or 'undefined'.
Destructuring into Existing Variables
When destructuring into varibales that have already been declared, the assignment expression must be wrapped in parentheses (). This is becuase the JavaScript engine would otherwise interpret the opening curly brace as the beginning of a block statement.
let userName, userAge;
const user = {
userName: 'Alex',
userAge: 32
};
({ userName, userAge } = user);
console.log(userName, userAge); // Alex, 32
Nested Destructuring
Destructuring is not limited to flat objects; you can also destructure nested properties. This is particularly useful for accessing deeply nested data without writing repetitive dot notation.
const user = {
userName: 'Alex',
userAge: 32,
address: {
city: 'New York',
zipCode: '10001'
}
};
// Extract the nested city property
const { address: { city } } = user;
console.log(city); // New York
It's important to note that attempting to destructure a nested property from a non-existent parent will throw an error.
const user = {
userName: 'Alex'
};
// This will throw an error because 'address' is undefined
const { address: { city } } = user;
// TypeError: Cannot destructure property 'city' of 'undefined' or 'null'.
Partial Destructuring
Destructuring assignments are evaluated from left to right. If an error occurs during the assignment of a later property, the assignment for the preceding properties may still complete successfully, resulting in a partially destructured object.
const user = {
userName: 'Alex',
userAge: 32
};
let displayName, role, yearsOld;
try {
// 'role' will cause an error as it doesn't exist, but 'userName' is assigned first
({ userName: displayName, role: { title }, userAge: yearsOld } = user);
} catch (e) {}
console.log(displayName, role, yearsOld); // Alex, undefined, undefined
Destructuring in Function Parameters
You can also use destructuring directly within a function's parameter list. This allows you to define function parameters that are automatically extracted from the passed object argument, making the function signature more expressive and the code inside the function cleaner.
const user = {
userName: 'Alex',
userAge: 32
};
function displayUserInfo(prefix, { userName, userAge }) {
console.log(prefix, userName, userAge);
}
function displayUserInfoRenamed(prefix, { userName: displayName, userAge: yearsOld }) {
console.log(prefix, displayName, yearsOld);
}
displayUserInfo('Info:', user); // Info: Alex 32
displayUserInfoRenamed('Details:', user); // Details: Alex 32