Enea Xharja

Software Engineer

Destructuring in JavaScript

Destructuring is a powerful feature introduced in ECMAScript 2015 (ES6) that allows data to be extracted from arrays and objects and assigned to variables in a more concise and readable way.

Historically, working with arrays and objects in JavaScript was cumbersome. Elements had to be accessed by index or property name, an operation that could be verbose and error-prone.

Objects

Suppose you want to add a single property to an object. With dot notation, you can add properties to an object only one at a time.

const character = {};
character.name = "Mario";
character.occupation = "Plumber";

console.log(character); // { name: 'Mario', occupation: 'Plumber' }

The same syntax can also be used to extract data one property at a time:

const character = {
  name: "Mario",
  occupation: "Plumber",
};

const name = character.name;
const occupation = character.occupation;

console.log(name); // Mario
console.log(occupation); // Plumber

If you want to add multiple properties to an object at once, you can use the object literal notation when initializing the object.

const character = {
  name: "Mario",
  occupation: "Plumber",
};

console.log(character); // { name: 'Mario', occupation: 'Plumber' }

Destructuring provides a more concise syntax for extracting multiple values from objects and arrays. Unlike array elements, object properties are not ordered, so they can be deconstructed in any order.

const character = {
  name: "Mario",
  occupation: "Plumber",
};

const { occupation, name } = character;

console.log(occupation); // Plumber
console.log(name); // Mario

Object desctructuring can be combined with computed properties when the property name is dynamic or not known in advance.

const character = {
  name: "Mario",
  occupation: "Plumber",
};

const key = "name";
const { [key]: value } = character;

console.log(value); // Mario

The value of [key] is computed at runtime by looking at the variable key. In this case, the value of key is "name", so the destructuring assignment is equivalent to const { name } = character. The value of name is then assigned to the variable value.

Results of function invocations

It is also possible to destructure the results of function invocations. For example, below we have a function called getCharacter() that returns the user object.

function getCharacter() {
  return {
    name: "Mario",
    occupation: "Plumber",
  };
}

const { name, occupation } = getCharacter();

console.log(name); // Mario
console.log(occupation); // Plumber

In the above example, we destructure the result of the getCharacter() function into the variables name and occupation.

Arrays

Destructuring can also be used with arrays. Below we have an array with some data:

const character = ["Mario", "Plumber", "Mushroom Kingdom"];

// Before destructuring
const name = character[0];
const occupation = character[1];
const location = character[2];

// With destructuring
const [name, occupation, location] = character;

console.log(name); // Mario
console.log(occupation); // Plumber
console.log(location); // Mushroom Kingdom

In addition, we can skip array indexes by excluding them completely from the destructuring syntax. Here is an example of so-called "sparse array destructuring."

const character = ["Mario", "Plumber", "Mushroom Kingdom"];

const [, occupation, location] = character;

console.log(occupation); // Plumber
console.log(location); // Mushroom Kingdom

Arrays are ordered collections of values. They have certain values at certain indexes. This is the structure of an array.

We use the destructuring syntax to "de-structure" the array and access the individual elements.

However, if we try to destructure a value that has no structure, such as null or undefined, we will get an error.

const character = null;

const [name, occupation, location] = character; // TypeError: character is not iterable

Default Values and Renaming

Destructuring makes the code more readable and maintainable, allowing variables to be renamed and default values to be provided for properties that may not exist.

const character = {
  n: "Mario",
  o: "Plumber",
  l: "Mushroom Kingdom",
};

const { n: name, o: occupation, l: location = "Unknown" } = character;

console.log(name); // Mario
console.log(occupation); // Plumber
console.log(location); // Mushroom Kingdom

In the previous example, we destructured the character object and renamed the n and o properties to name and occupation respectively. We also renamed the l property to location and provided a default value of "Unknown" in case the location property is not present in the object.

Function arguments and parameters

When defining functions, one may be faced with problems such as the need to pass a large number of arguments and the need to provide default values for some of these arguments. Also, if the order of the arguments changes, function calls must be updated accordingly.

Destructuring can help solve these problems by allowing objects to be passed as arguments to functions.

function printCharacter({
  name = "",
  location = "Unknown Location",
  occupation = "",
}) {
  console.log(`${name} is a ${occupation} from ${location}.`);
}

const character = {
  name: "Mario",
  occupation: "Plumber",
  location: "Mushroom Kingdom",
};

printCharacter(character); // Mario is a Plumber from Mushroom Kingdom.

In this way, the order of the arguments does not matter, and default values can be provided for missing properties.

Rest Syntax

The rest syntax (...) allows the remaining elements of an array or object to be collected into a new array or object.

const character = ["Mario", "Plumber", "Mushroom Kingdom", "Super Mario Bros"];

const [name, occupation, ...others] = character;

console.log(name); // Mario
console.log(occupation); // Plumber
console.log(others); // [ 'Mushroom Kingdom', 'Super Mario Bros' ]

In the above example, the ...others syntax collects the remaining elements of the character array into a new array called others.

Note that the rest syntax must be the last element of the destructuring assignment. For example, the following code will result in an error:

const [name, ...others, occupation] = character; // SyntaxError: Rest element must be last element

The rest syntax can also be used with objects.

const character = {
  name: "Mario",
  occupation: "Plumber",
  location: "Mushroom Kingdom",
  game: "Super Mario Bros",
};

const { name, occupation, ...others } = character;

console.log(name); // Mario
console.log(occupation); // Plumber
console.log(others); // { location: 'Mushroom Kingdom', game: 'Super Mario Bros' }

In the above example, the ...others syntax collects the remaining properties of the character object that have not been explicitly matched into a new object called others.

Strings

Destructuring can also be used with strings. When destructuring a string, each character is treated as an element of an array. Rest parameters also work with strings, providing an array of individual characters.

const character = "Mario";
const [...characters] = character;

console.log(characters); // [ 'M', 'a', 'r', 'i', 'o' ]

In the above example, the ...characters syntax collects the individual characters of the character string into a new array called characters with each character as a separate element.

Nested Destructuring

We can destructure nested data, as arrays within other arrays or objects within other objects.

const character = {
  name: "Mario",
  occupation: "Plumber",
  location: {
    name: "Mushroom Kingdom",
    population: 1000,
  },
};

const {
  name,
  occupation,
  location: { name: locationName, population },
} = character;

console.log(name); // Mario
console.log(occupation); // Plumber
console.log(locationName); // Mushroom Kingdom
console.log(population); // 1000

In the previous example, we destructure the character object and extract the properties name, occupation, locationName and population.

Let us now look at an example of nested destructuring with arrays.

const character = ["Mario", "Plumber", ["Mushroom Kingdom", 1000]];

This time, the character array contains another array with the location name and population. Here we want to extract only the population value.

const [, , [, population]] = character;
console.log(population); // 1000

We destructured the character array and extracted the population value from the nested array. We used empty commas to skip the first two elements of the character array and then destructured the nested array to extract the population value.

Destructuring also works on arrays and objects nested within each other.

const characters = [
  {
    name: "Mario",
    occupation: "Plumber",
    location: {
      name: "Mushroom Kingdom",
      population: 1000,
    },
  },
  {
    name: "Luigi",
    occupation: "Plumber",
    location: {
      name: "Mushroom Kingdom",
      population: 1000,
    },
  },
];

const [, { name }] = characters;

console.log(name); // Luigi

In this case, we destructured the characters array and extracted the name property of the second object in the array.