What is object destructuring?
Object Destructuring
Object destructuring provides a convenient syntax for pulling properties out of an object and into variables.
Here is a quick example using a book object:
const book = {
title: 'Dune',
author: 'Frank Herbert',
yearPublished: 1965,
genre: 'Science Fiction',
};
const { title, author } = book;
console.log(title); // ‘Dune’
console.log(author); // ‘Frank Herbert’This is essentially a shorthand for the following:
const title = book.title;
const author = book.author;You can extract any number of properties you need.
Renaming Extracted Values
Let's look at a scenario where a variable name might conflict:
const title = 'Some Other Title';
const book = { title: 'Dune' };
const { title } = book;
// Uncaught SyntaxError:
// Identifier 'title' has already been declaredHere, we encounter a problem: a variable named title already exists. To handle this, you can assign the property to a new variable name during destructuring:
const title = 'Some Other Title';
const book = { title: 'Dune' };
const { title: bookTitle } = book;
console.log(title); // ‘Some Other Title’
console.log(bookTitle); // ‘Dune’Default Values
What occurs when you try to destructure a property that doesn't exist on the object?
const book = { title: 'Dune' };
const { genre } = book;
console.log(genre); // ???Since book.genre is not defined, the genre variable will be assigned the value undefined. You can provide a fallback value by using the equals sign (=):
const { genre = 'Science Fiction' } = book;If the book object contains a genre property, its value will be assigned to the new genre constant. If not, genre will be assigned the string "Science Fiction". This is a more concise way of writing the traditional check:
const genre =
typeof book.genre === 'undefined'
? 'Science Fiction'
: book.genre;Destructuring Function Parameters
Imagine a function that accepts an object as an argument:
function displayProduct(product) {
if (!product.name) {
console.error('Product name is missing!');
}
if (product.price < 0) {
console.error('Price cannot be negative!');
}
// ...
}One option is to destructure the required properties at the beginning of the function body:
function displayProduct(product) {
const { name, price } = product;
if (!name) {
console.error('Product name is missing!');
}
if (price < 0) {
console.error('Price cannot be negative!');
}
// ...
}Alternatively, parameter destructuring allows you to do this directly in the function's signature:
function displayProduct({ name, price }) {
if (!name) {
console.error('Product name is missing!');
}
if (price < 0) {
console.error('Price cannot be negative!');
}
// ...
}All three versions are functionally identical. However, many developers prefer parameter destructuring for its conciseness. It's a very common pattern in frameworks like React for handling component props.
Named Arguments
In some other programming languages, "named arguments" allow developers to specify arguments by name when calling a function. JavaScript lacks this feature built-in, which can sometimes make function calls with multiple arguments confusing. For instance, you might see this in a codebase:
drawShape('rectangle', 50, 100, '#00F');What do 50 and 100 represent here? Their purpose is unclear without looking up the function's definition. However, if the drawShape function were written to accept a single object, the call would look like this:
drawShape({
type: 'rectangle',
width: 50,
height: 100,
color: '#00F',
});By passing a single object, each property acts as a named label for its value. This technique effectively simulates named arguments and makes the code much easier to understand at a glance.
Renaming Function Parameters
As we saw earlier, you can rename variables during standard object destructuring. This same renaming syntax can also be applied directly to function parameters. An example will make this clear:
function getProfileLink({ username: userId }) {
return `https://example.com/users/${userId}`;
}
getProfileLink({ username: 'jane_doe' });Here, we pass an object { username: 'jane_doe' } to the function. We extract the username property but rename it to userId. Inside the getProfileLink function, the variable userId holds the value jane_doe, while username is not defined.
Default Parameter Values
Just as with regular object destructuring, you can assign default values to destructured function parameters.
function createChart({ type = 'line', showGridlines }) {
// ...
}When calling this function, you can provide your own value for type:
createChart({ type: 'bar', showGridlines: false });Alternatively, if you want to use the default line value, you can simply leave it out of the object:
createChart({ showGridlines: true });Gotcha: Argument Must Be Provided
Let's add default values for both properties in our function:
function createChart({
type = 'line',
showGridlines = true,
}) {
// ...
}Now, what if we call the function with no arguments at all, hoping to rely on the defaults?
createChart();This will cause an error: Uncaught TypeError: Cannot read properties of undefined (reading 'type') The issue is that the function expects an object to destructure from. When we call createChart() without arguments, the parameter is undefined, and you cannot destructure properties from undefined. The solution is to provide a default value for the entire object parameter itself—an empty object ():
function createChart({
type = 'line',
showGridlines = true,
} = {}) {
// ...
}
createChart(); // ✅ This works!Now, when createChart is called with no arguments, its parameter defaults to {}. The destructuring then proceeds on this empty object, correctly applying the default values for type and showGridlines.