Exploring Destructuring in JavaScript

Exploring Destructuring in JavaScript

The Complete guide you need for Destructuring in JavaScript

Β·

8 min read

What is Destructuring?

Destructuring is a special really cool syntax feature in JavaScript, which lets us to extract values from arrays, objects, or other iterable structures and assign them to variables.

It's a shorthand way to access properties or elements of a data structure without having to use dot notation or array indices.

How is it beneficial for us (who write code in JavaScript)?

Destructuring has several benefits that make our code more concise, readable, and maintainable!

  • Improved Readability: Destructuring simplifies code by reducing the need for complex variable assignments and dot notation.

  • Less Boilerplate Code: You can extract values directly from data structures without needing to create intermediate variables.

  • More Concise Code: Destructuring can reduce the number of lines of code needed to achieve the same result.

  • Flexibility: You can destructure data structures of any type (objects, arrays, iterables), making it a versatile tool in your JavaScript toolkit.

Effective destructuring πŸš€ enables us to write more expressive, maintainable, and efficient code that's easier to understand and debug.

Basic Example

const person = { name: 'John', age: 30 };
const { name, age } = person;
console.log(name); // "John"
console.log(age); // 30

Here we have destructured an object person with two properties: name and age.

When destructuring an JavaScript object, the values we extract must be the exact same keys in the object. You can't place userName in place of name in the line const { name, age } = person;. Which simply means - const { userName, age } = person; won't work. But yes! We can apply aliasing while destructuring an object. E.G. -

const person = { name: 'John', age: 30 };
const { name:userName, age:userAge } = person;
console.log(userName); // "John"
console.log(userAge); // 30

Most probably you have seen destructuring an object for the first time when you were importing a module. For example when importing the exec function -

import { exec } from "node:child_process"; // ES Module syntax
const { exec } = require("child_process"); // commonJS syntax

Similarly we can destructure arrays also -

const numbers = [4, 5, 6];
const [x, y, z] = numbers;
console.log(x); // 4
console.log(y); // 5
console.log(z); // 6

Here when destructuring arrays, you don't need to use aliasing to assign any element to a custom variable name. Because array elements are simply just values, they aren't bound with some keys.

Default Values

Destructuring allows you to assign default values to variables if the property doesn't exist in the object.

const person = { name: 'John' };
const { name = 'Anonymous', age } = person; // age will be undefined
console.log(name); // "John"
console.log(age); // undefined

Here the string value 'John' wasn't substituted by the value 'Anonymous'in the variable name because it already existed in the object. Whereas -

const person = { name: 'John' };
const { name, age = 30 } = person; // age defaults to 30 if not present
console.log(name); // "John"
console.log(age); // 30

Spread Syntax

The spread syntax or say operator(...) can be used with destructuring to capture remaining elements of an array or properties of an object into a new variable.

  • spread syntax with Arrays -
const numbers = [1, 2, 3, 4, 5];
const [first, second, ...rest] = numbers;
console.log(first);  // 1
console.log(second); // 2
console.log(rest);    // [3, 4, 5] (remaining elements)
  • spread syntax with Objects -
const person = { name: 'John', age: 30, city: 'New York' };
const { name, ...info } = person;
console.log(name);  // "John"
console.log(info);   // { age: 30, city: "New York"} (remaining properties)

Nested Destructuring

Destructuring can be nested to extract values from deeply nested objects or arrays.

const data = {
  user: {
    name: 'Alicia',
    origin: 'Romania',
    eyes: 'blue',
    address: {
      city: 'London',
    }
  }
};

const { user: { name, address: { city } } } = data;
console.log(name);  // "Alicia"
console.log(city);   // "London"

Destructuring in function parameter list

Suppose we've an JavaScript object named credentials -

const credentials = {
  name: 'Debajyati',
  age: 20,
  address: {
    city: 'Kolkata',
    state: 'West Bengal',
    country: 'India'
  },
  phone: '',
  email: '',
  hobbies: ['reading', 'listening to music', 'coding', 'watching Anime'],
  skills: {
    programming: true,
    blogging: true,
    singing: false
  }
}

And a function named showCredentials which takes only 1 argument value which is an object and Standard outputs a string based on some of the object properties. Well, we could write the function definition in this way -

function showCredential(obj) {
  const hasSkill = (skill) => obj.skills[skill];
  console.log(
    `${obj.name} is ${obj.age} years old.\n Lives in ${obj.address.city}, ${obj.address.country}.\n`,
    `He has the following hobbies: ${obj.hobbies.join(", ")}`,
  );
  if (hasSkill("programming")) {
    console.log(`He is a programmer.`);
  }
  if (hasSkill("singing")) {
    console.log(`He is a singer.`);
  }
  if (hasSkill("blogging")) {
    console.log(`He is also a tech blogger.`);
  }
}

Calling it with -

showCredential(credentials);

Getting this output -

Debajyati is 20 years old.
 Lives in Kolkata, India.
 He has the following hobbies: reading, listening to music, coding, watch
ing Anime
He is a programmer.
He is also a tech blogger.

Instead we can destructure the object argument in the parameter list while defining the function. Like this -

function showCredential({ name, age, address: { city, country}, hobbies, skills }) {
  const hasSkill = (skill) => skills[skill];
  console.log(
    `${name} is ${age} years old.\n Lives in ${city}, ${country}.\n`,
    `He has the following hobbies: ${hobbies.join(", ")}`,
  );
  if (hasSkill("programming")) {
    console.log(`He is a programmer.`);
  }
  if (hasSkill("singing")) {
    console.log(`He is a singer.`);
  }
  if (hasSkill("blogging")) {
    console.log(`He is also a tech blogger.`);
  }
}

which gives the same output.

πŸ’‘ NOTE
The function still takes only one argument. Destructuring didn't increase number of arguments in the function parameter list.

Also, calling the function didn't change as well. It still is -

showCredential(credentials);

So, Why destructure objects in Function Parameter List?

While destructuring in function arguments list may seem cumbersome or tedious at first but it has it's quite important benefits.

Important Points to Consider

  • Safer Code: Destructuring can help prevent errors by making it clear which properties are expected by the function. If a property is missing in the passed object, destructuring will result in an error during function execution, aiding in early detection of potential issues.

  • Reduced Verbosity: By directly extracting properties into variables within the parameter list, you avoid repetitive object property access using dot notation. This leads to cleaner and more concise function definitions.

  • Focus on Functionality: By destructuring within the parameter list, you separate data access logic from the function's core functionality. This improves code organization and makes the function's purpose clearer.

Destructuring Strings

Just how we destructure arrays, similarly we can also unpack strings as array elements. A clever usage of our intelligence.

const fruit = 'grape';
const [first, second, ...rest] = fruit;
const animal = rest.join('');
console.log(animal); // ape
⚠️ Remember !
When you use the spread operator (...) to capture the remaining characters from a string, you don't get a string. You get an array of those characters.

Some Handy Application Examples of Destructuring

  • Destructuring for swapping without 3rd variable: JavaScript traditionally required a temporary variable to swap the values of two variables. Destructuring offers a more concise and readable way to achieve this.

    • Before Destructuring:

        let a = 10;
        let b = 20;
      
        let temp = a;
        a = b;
        b = temp;
        console.log(a, b); // Output: 20 10
      
    • After Destructuring:

        let a = 10;
        let b = 20;
      
        [a, b] = [b, a];
      
        console.log(a, b); // Output: 20 10
      

So nifty & elegant✨! Isn't it?

  • Destructuring Function Return Values: Functions can return multiple values as an array or object. Destructuring allows you to unpack these returned values into separate variables, improving code clarity. Let's suppose you have a function that fetches data from an API and returns a response object:

      function getUserUpdates(id) {
        // Simulating some API call with a GET request
        return {
          data: {
            player: response.group.names[id],
            brain: "rotting",
            powerLevel: Number(response.group.power[id]),
            useAsDecoy: true,
          },
          statusCode: Number(response.status),
        };
      }
    

In the context of building APIs or handling server responses, it offers distinct advantages that enhance code quality and maintainability. Accessing individual properties is going to be breeze, because you can directly extract the properties you need from the function's return value into separate variables during the function call itself.

const {
  data: {player, useAsDecoy, powerLevel},
  statusCode,
} = getUserUpdates(1);

Whenever a function returns an object and you are interested in specific property values, always apply destructuring straight away. If you're still thinking destructuring in return values isn't a good idea, these 2 more advantages may convince you -

1. Simplified Mental Model: Destructuring simplifies the thought process required to understand data flow for the developer who will be using your function. Instead of memorizing intricate property access chains, developers can focus on the meaning conveyed by the variable names used in the destructuring pattern. This reduces cognitive load and promotes better code comprehension.

2. Reduced Boilerplate Code for Complex Return Objects: When functions return objects with numerous or nested properties, destructuring significantly reduces the boilerplate code needed to access them individually. This leads to a more concise and less cluttered codebase, improving overall code quality.

  • Destructuring with Conditions:Destructuring can be combined with conditional statements to handle different scenarios based on the structure of an object. If you've a function that receives an object with optional properties:

      function greetUser(user) {
        const { name = "Anonymous" } = user || {}; // Destructuring with default value
    
        console.log(`Hello, ${name}!`);
      }
    
      greetUser({ name: "Bob" });       // Output: "Hello, Bob!"
      greetUser({});                    // Output: "Hello, Anonymous!" (no name property)
      greetUser(undefined);           // Output: "Hello, Anonymous!" (function receives no argument)
    

Conclusion

Throughout the whole article, we've learnt that 'Destructuring' is a powerful and versatile feature in JavaScript that can significantly improve your code's readability, maintainability, and efficiency. By effectively using destructuring techniques, you can write cleaner, more concise, and less error-prone code. So, embrace destructuring and take your JavaScript skills to the next level!

If you found this POST helpful, if this blog added some value to your time and energy, please show some love by giving the article some likes and share it with your friends.

Feel free to connect with me at - Twitter, LinkedIn or GitHub :)

Happy Coding πŸ§‘πŸ½β€πŸ’»πŸ‘©πŸ½β€πŸ’»! Have a nice day ahead! πŸš€

Did you find this article valuable?

Support Debajyati's Blogs by becoming a sponsor. Any amount is appreciated!

Β