HomepageExploring JavaScript (ES2024 Edition)
You can support this book: buy it or donate

## 39 Destructuring [ES6]

### 39.1 A first taste of destructuring

With normal assignment, you extract one piece of data at a time – for example:

````const arr = ['a', 'b', 'c'];`
`const x = arr[0]; // extract`
`const y = arr[1]; // extract`
```

With destructuring, you can extract multiple pieces of data at the same time via patterns in locations that receive data. The left-hand side of `=` in the previous code is one such location. In the following code, the square brackets in line A are a destructuring pattern:

````const arr = ['a', 'b', 'c'];`
`const [x, y] = arr; // (A)`
`assert.equal(x, 'a');`
`assert.equal(y, 'b');`
```

This code does the same as the previous code.

Note that the pattern is “smaller” than the data: we are only extracting what we need.

### 39.2 Constructing vs. extracting

In order to understand what destructuring is, consider that JavaScript has two kinds of operations that are opposites:

• You can construct compound data, for example, by setting properties and via object literals.
• You can extract data out of compound data, for example, by getting properties.

Constructing data looks as follows:

````// Constructing: one property at a time`
`const jane1 = {};`
`jane1.first = 'Jane';`
`jane1.last = 'Doe';`

`// Constructing: multiple properties`
`const jane2 = {`
`  first: 'Jane',`
`  last: 'Doe',`
`};`

`assert.deepEqual(jane1, jane2);`
```

Extracting data looks as follows:

````const jane = {`
`  first: 'Jane',`
`  last: 'Doe',`
`};`

`// Extracting: one property at a time`
`const f1 = jane.first;`
`const l1 = jane.last;`
`assert.equal(f1, 'Jane');`
`assert.equal(l1, 'Doe');`

`// Extracting: multiple properties (NEW!)`
`const {first: f2, last: l2} = jane; // (A)`
`assert.equal(f2, 'Jane');`
`assert.equal(l2, 'Doe');`
```

The operation in line A is new: we declare two variables `f2` and `l2` and initialize them via destructuring (multivalue extraction).

The following part of line A is a destructuring pattern:

````{first: f2, last: l2}`
```

Destructuring patterns are syntactically similar to the literals that are used for multivalue construction. But they appear where data is received (e.g., at the left-hand sides of assignments), not where data is created (e.g., at the right-hand sides of assignments).

### 39.3 Where can we destructure?

Destructuring patterns can be used at “data sink locations” such as:

• Variable declarations:

````const [a] = ['x'];`
`assert.equal(a, 'x');`

`let [b] = ['y'];`
`assert.equal(b, 'y');`
```
• Assignments:

````let b;`
`[b] = ['z'];`
`assert.equal(b, 'z');`
```
• Parameter definitions:

````const f = ([x]) => x;`
`assert.equal(f(['a']), 'a');`
```

Note that variable declarations include `const` and `let` declarations in `for-of` loops:

````const arr = ['a', 'b'];`
`for (const [index, element] of arr.entries()) {`
`    console.log(index, element);`
`}`
```

Output:

````0 a`
`1 b`
```

In the next two sections, we’ll look deeper into the two kinds of destructuring: object-destructuring and Array-destructuring.

### 39.4 Object-destructuring

Object-destructuring lets you batch-extract values of properties via patterns that look like object literals:

````const address = {`
`  street: 'Evergreen Terrace',`
`  number: '742',`
`  city: 'Springfield',`
`  state: 'NT',`
`  zip: '49007',`
`};`

`const { street: s, city: c } = address;`
`assert.equal(s, 'Evergreen Terrace');`
`assert.equal(c, 'Springfield');`
```

You can think of the pattern as a transparent sheet that you place over the data: the pattern key `'street'` has a match in the data. Therefore, the data value `'Evergreen Terrace'` is assigned to the pattern variable `s`.

You can also object-destructure primitive values:

````const {length: len} = 'abc';`
`assert.equal(len, 3);`
```

And you can object-destructure Arrays:

````const {0:x, 2:y} = ['a', 'b', 'c'];`
`assert.equal(x, 'a');`
`assert.equal(y, 'c');`
```

Why does that work? Array indices are also properties.

#### 39.4.1 Property value shorthands

Object literals support property value shorthands and so do object patterns:

````const { street, city } = address;`
`assert.equal(street, 'Evergreen Terrace');`
`assert.equal(city, 'Springfield');`
```

Exercise: Object-destructuring

`exercises/destructuring/object_destructuring_exrc.mjs`

#### 39.4.2 Rest properties

In object literals, you can have spread properties. In object patterns, you can have rest properties (which must come last):

````const obj = { a: 1, b: 2, c: 3 };`
`const { a: propValue, ...remaining } = obj; // (A)`

`assert.equal(propValue, 1);`
`assert.deepEqual(remaining, {b:2, c:3});`
```

A rest property variable, such as `remaining` (line A), is assigned an object with all data properties whose keys are not mentioned in the pattern.

`remaining` can also be viewed as the result of non-destructively removing property `a` from `obj`.

#### 39.4.3 Syntax pitfall: assigning via object destructuring

If we object-destructure in an assignment, we are facing a pitfall caused by syntactic ambiguity – you can’t start a statement with a curly brace because then JavaScript thinks you are starting a block:

````let prop;`
`assert.throws(`
`  () => eval("{prop} = { prop: 'hello' };"),`
`  {`
`    name: 'SyntaxError',`
`    message: "Unexpected token '='",`
`  });`
```

Why `eval()`?

`eval()` delays parsing (and therefore the `SyntaxError`) until the callback of `assert.throws()` is executed. If we didn’t use it, we’d already get an error when this code is parsed and `assert.throws()` wouldn’t even be executed.

The workaround is to put the whole assignment in parentheses:

````let prop;`
`({prop} = { prop: 'hello' });`
`assert.equal(prop, 'hello');`
```

### 39.5 Array-destructuring

Array-destructuring lets you batch-extract values of Array elements via patterns that look like Array literals:

````const [x, y] = ['a', 'b'];`
`assert.equal(x, 'a');`
`assert.equal(y, 'b');`
```

You can skip elements by mentioning holes inside Array patterns:

````const [, x, y] = ['a', 'b', 'c']; // (A)`
`assert.equal(x, 'b');`
`assert.equal(y, 'c');`
```

The first element of the Array pattern in line A is a hole, which is why the Array element at index 0 is ignored.

#### 39.5.1 Array-destructuring works with any iterable

Array-destructuring can be applied to any value that is iterable, not just to Arrays:

````{ // Sets are iterable`
`  const [a, b] = new Set().add('fee').add('fi').add('fo');`
`  assert.equal(a, 'fee');`
`  assert.equal(b, 'fi');`
`}`

`{ // Maps are iterable`
`  const [a, b] = new Map().set('one', 1).set('two', 2);`
`  assert.deepEqual(`
`    a, ['one',1]`
`  );`
`  assert.deepEqual(`
`    b, ['two',2]`
`  );`
`}`

`{ // Strings are iterable`
`  const [a, b] = 'hello';`
`  assert.equal(a, 'h');`
`  assert.equal(b, 'e');`
`}`
```

#### 39.5.2 Rest elements

In Array literals, you can have spread elements. In Array patterns, you can have rest elements (which must come last):

````const [x, y, ...remaining] = ['a', 'b', 'c', 'd']; // (A)`

`assert.equal(x, 'a');`
`assert.equal(y, 'b');`
`assert.deepEqual(remaining, ['c', 'd']);`
```

A rest element variable, such as `remaining` (line A), is assigned an Array with all elements of the destructured value that were not mentioned yet.

### 39.6 Examples of destructuring

#### 39.6.1 Array-destructuring: swapping variable values

You can use Array-destructuring to swap the values of two variables without needing a temporary variable:

````let x = 'a';`
`let y = 'b';`

`[x,y] = [y,x]; // swap`

`assert.equal(x, 'b');`
`assert.equal(y, 'a');`
```

#### 39.6.2 Array-destructuring: operations that return Arrays

Array-destructuring is useful when operations return Arrays, as does, for example, the regular expression method `.exec()`:

````// Skip the element at index 0 (the whole match):`
`const [, year, month, day] =`
`  /^([0-9]{4})-([0-9]{2})-([0-9]{2})\$/`
`  .exec('2999-12-31');`

`assert.equal(year, '2999');`
`assert.equal(month, '12');`
`assert.equal(day, '31');`
```

#### 39.6.3 Object-destructuring: multiple return values

Destructuring is very useful if a function returns multiple values – either packaged as an Array or packaged as an object.

Consider a function `findElement()` that finds elements in an Array:

````findElement(array, (value, index) => «boolean expression»)`
```

Its second parameter is a function that receives the value and index of an element and returns a boolean indicating if this is the element the caller is looking for.

We are now faced with a dilemma: Should `findElement()` return the value of the element it found or the index? One solution would be to create two separate functions, but that would result in duplicated code because both functions would be very similar.

The following implementation avoids duplication by returning an object that contains both index and value of the element that is found:

````function findElement(arr, predicate) {`
`  for (let index=0; index < arr.length; index++) {`
`    const value = arr[index];`
`    if (predicate(value)) {`
`      // We found something:`
`      return { value, index };`
`    }`
`  }`
`  // We didn’t find anything:`
`  return { value: undefined, index: -1 };`
`}`
```

Destructuring helps us with processing the result of `findElement()`:

````const arr = [7, 8, 6];`

`const {value, index} = findElement(arr, x => x % 2 === 0);`
`assert.equal(value, 8);`
`assert.equal(index, 1);`
```

As we are working with property keys, the order in which we mention `value` and `index` doesn’t matter:

````const {index, value} = findElement(arr, x => x % 2 === 0);`
```

The kicker is that destructuring also serves us well if we are only interested in one of the two results:

````const arr = [7, 8, 6];`

`const {value} = findElement(arr, x => x % 2 === 0);`
`assert.equal(value, 8);`

`const {index} = findElement(arr, x => x % 2 === 0);`
`assert.equal(index, 1);`
```

All of these conveniences combined make this way of handling multiple return values quite versatile.

### 39.7 What happens if a pattern part does not match anything?

What happens if there is no match for part of a pattern? The same thing that happens if you use non-batch operators: you get `undefined`.

#### 39.7.1 Object-destructuring and missing properties

If a property in an object pattern has no match on the right-hand side, you get `undefined`:

````const {prop: p} = {};`
`assert.equal(p, undefined);`
```

#### 39.7.2 Array-destructuring and missing elements

If an element in an Array pattern has no match on the right-hand side, you get `undefined`:

````const [x] = [];`
`assert.equal(x, undefined);`
```

### 39.8 What values can’t be destructured?

#### 39.8.1 You can’t object-destructure `undefined` and `null`

Object-destructuring only fails if the value to be destructured is either `undefined` or `null`. That is, it fails whenever accessing a property via the dot operator would fail too.

````> const {prop} = undefined`
`TypeError: Cannot destructure property 'prop' of 'undefined'`
`as it is undefined.`

`> const {prop} = null`
`TypeError: Cannot destructure property 'prop' of 'null'`
`as it is null.`
```

#### 39.8.2 You can’t Array-destructure non-iterable values

Array-destructuring demands that the destructured value be iterable. Therefore, you can’t Array-destructure `undefined` and `null`. But you can’t Array-destructure non-iterable objects either:

````> const [x] = {}`
`TypeError: {} is not iterable`
```

All of the remaining sections are advanced.

### 39.10 Default values

Normally, if a pattern has no match, the corresponding variable is set to `undefined`:

````const {prop: p} = {};`
`assert.equal(p, undefined);`
```

If you want a different value to be used, you need to specify a default value (via `=`):

````const {prop: p = 123} = {}; // (A)`
`assert.equal(p, 123);`
```

In line A, we specify the default value for `p` to be `123`. That default is used because the data that we are destructuring has no property named `prop`.

#### 39.10.1 Default values in Array-destructuring

Here, we have two default values that are assigned to the variables `x` and `y` because the corresponding elements don’t exist in the Array that is destructured.

````const [x=1, y=2] = [];`

`assert.equal(x, 1);`
`assert.equal(y, 2);`
```

The default value for the first element of the Array pattern is `1`; the default value for the second element is `2`.

#### 39.10.2 Default values in object-destructuring

You can also specify default values for object-destructuring:

````const {first: f='', last: l=''} = {};`
`assert.equal(f, '');`
`assert.equal(l, '');`
```

Neither property key `first` nor property key `last` exist in the object that is destructured. Therefore, the default values are used.

With property value shorthands, this code becomes simpler:

````const {first='', last=''} = {};`
`assert.equal(first, '');`
`assert.equal(last, '');`
```

### 39.11 Parameter definitions are similar to destructuring

Considering what we have learned in this chapter, parameter definitions have much in common with an Array pattern (rest elements, default values, etc.). In fact, the following two function declarations are equivalent:

````function f1(«pattern1», «pattern2») {`
`  // ···`
`}`

`function f2(...args) {`
`  const [«pattern1», «pattern2»] = args;`
`  // ···`
`}`
```

### 39.12 Nested destructuring

Until now, we have only used variables as assignment targets (data sinks) inside destructuring patterns. But you can also use patterns as assignment targets, which enables you to nest patterns to arbitrary depths:

````const arr = [`
`  { first: 'Jane', last: 'Bond' },`
`  { first: 'Lars', last: 'Croft' },`
`];`
`const [, {first}] = arr; // (A)`
`assert.equal(first, 'Lars');`
```

Inside the Array pattern in line A, there is a nested object pattern at index 1.

Nested patterns can become difficult to understand, so they are best used in moderation.