Клавиша / esc

Деструктуризация

Извлекаем свойства объектов в отдельные переменные.

Время чтения: 10 мин

Кратко

Скопировано

Деструктуризация — это синтаксис, позволяющий упростить присваивание переменным значений свойств объекта или элементов массива. Для краткости будем использовать общепринятый термин «деструктуризация», хотя в спецификации ECMAScript используется понятие деструктурирующее присваивание (Destructuring assignment).

Рассмотрим простой пример. Нужно извлечь из объекта person поля name и age.

        
          
          const person = {name: 'Александр', age: '37'}
          const person = {name: 'Александр', age: '37'}

        
        
          
        
      

Это можно сделать так:

        
          
          const name = person.nameconst age = person.age
          const name = person.name
const age = person.age

        
        
          
        
      

Используя деструктуризацию, можно сократить код до:

        
          
          const {name, age} = person
          const {name, age} = person

        
        
          
        
      

А вот пример с массивом. Извлечём из массива planets три первых элемента.

        
          
          const planets = ['Меркурий', 'Венера', 'Земля', 'Марс']
          const planets = ['Меркурий', 'Венера', 'Земля', 'Марс']

        
        
          
        
      

Это можно сделать, получая значения по индексу:

        
          
          const first = planets[0]const second = planets[1]const third = planets[2] // Мы тут!
          const first = planets[0]
const second = planets[1]
const third = planets[2] // Мы тут!

        
        
          
        
      

А можно, используя деструктуризацию:

        
          
          const [first, second, third] = planets
          const [first, second, third] = planets

        
        
          
        
      

Как пишется

Скопировано

Синтаксис деструктуризации во многом похож на определение объектов или массивов с использованием литералов, но при этом [] или {} будут находиться слева от оператора присваивания:

        
          
          const [element] = array // element = array[0]let {field} = obj // field = obj.field
          const [element] = array // element = array[0]
let {field} = obj // field = obj.field

        
        
          
        
      

Деструктуризация объектов

Скопировано

Пока что разобрали простые примеры. Преимущества и гибкость деструктуризации раскрываются во всей полноте в сложных случаях.

Для извлекаемых свойств объекта можно предусмотреть значения по умолчанию. Это может понадобиться, если объект не содержит полей с указанными именами.

        
          
          const point = {x: 120, y: 30}const {x, y, color = 'black'} = pointconsole.log(x, y)// 120 30console.log(color)// black
          const point = {x: 120, y: 30}

const {x, y, color = 'black'} = point

console.log(x, y)
// 120 30

console.log(color)
// black

        
        
          
        
      

Допустим, объект содержит свойства, которые мы хотим извлечь и записать в переменные под другими именами. Вот как это выполнить:

        
          
          const shape = {'name.ru': 'квадрат', 'color-str': 'black'}const {  'name.ru': name,  'color-str': color} = shapeconsole.log(name, color)// квадрат black
          const shape = {'name.ru': 'квадрат', 'color-str': 'black'}

const {
  'name.ru': name,
  'color-str': color
} = shape

console.log(name, color)
// квадрат black

        
        
          
        
      

Имена свойств, которые извлекаем из объекта, можно указывать динамически. Выполним это вот так:

        
          
          const record = {id: '#2514', value: 45, format: 'число', type: 'a'}// Имя извлекаемого свойстваconst key = 'value'const {id, [key]: recordValue} = recordconsole.log(id, ":", recordValue)// #2514 : 45
          const record = {id: '#2514', value: 45, format: 'число', type: 'a'}

// Имя извлекаемого свойства
const key = 'value'

const {id, [key]: recordValue} = record

console.log(id, ":", recordValue)
// #2514 : 45

        
        
          
        
      

Оператор rest (...) может собрать неизвлечённые свойства в новый объект. Этот приём удобен, когда получаем объект, созданный на основе исходного объекта, за исключением указанных полей:

        
          
          const point = {x: 120, y: 30, color: 'blue', opacity: 0.4}const {color, opacity, ...restPoint} = pointconsole.log(restPoint)// { x: 120, y: 30 }
          const point = {x: 120, y: 30, color: 'blue', opacity: 0.4}

const {color, opacity, ...restPoint} = point

console.log(restPoint)
// { x: 120, y: 30 }

        
        
          
        
      

Деструктуризация может выполняться для объектов любой вложенности. Допустим, у нас есть такой объект:

        
          
          const creator = {  person: {    name: {      firstName: 'Брендан',      lastName: 'Эйх'    },    year: 1961  },  creation: 'JavaScript'}
          const creator = {
  person: {
    name: {
      firstName: 'Брендан',
      lastName: 'Эйх'
    },
    year: 1961
  },
  creation: 'JavaScript'
}

        
        
          
        
      

Извлекаем свойства вложенных объектов:

        
          
          // Извлечём свойства firstName, year, creation.// Переменные person и name не создаютсяconst {person: {name: {firstName}, year}, creation} = creatorconsole.log(firstName, year, creation)// Брендан 1961 JavaScript
          // Извлечём свойства firstName, year, creation.
// Переменные person и name не создаются
const {person: {name: {firstName}, year}, creation} = creator

console.log(firstName, year, creation)
// Брендан 1961 JavaScript

        
        
          
        
      

С помощью деструктуризации можно не только извлечь указанные в объекте свойства, но и те, которые установлены в прототипах. Извлечём name (свойство объекта node), метод setParent (определён в прототипе Node) и valueOf (определён в прототипе Object):

        
          
          // Функция-конструкторfunction Node(name) {  this.name = `#${name}`}// Указанный в прототипе метод setParentNode.prototype.setParent = parentNode =>  this.parent = parent// Создаём объектconst node = new Node(24)// Извлекаем свойстваconst {name, setParent, valueOf} = nodeconsole.log(name)// #24console.log(setParent)// [Function (anonymous)]console.log(valueOf)// [Function: valueOf]
          // Функция-конструктор
function Node(name) {
  this.name = `#${name}`
}

// Указанный в прототипе метод setParent
Node.prototype.setParent = parentNode =>
  this.parent = parent

// Создаём объект
const node = new Node(24)

// Извлекаем свойства
const {name, setParent, valueOf} = node

console.log(name)
// #24

console.log(setParent)
// [Function (anonymous)]

console.log(valueOf)
// [Function: valueOf]

        
        
          
        
      

При деструктуризации объявление переменных с помощью let или const обычно происходит вместе с их назначением:

        
          
          const {foo, bar} = obj
          const {foo, bar} = obj

        
        
          
        
      

Если определили переменные ранее, нужно обернуть операцию в скобки:

        
          
          let foo// ⚠️ Внимание! Без `;` в конце строки будет ошибкаlet bar;// ...( { foo, bar } = obj )
          let foo
// ⚠️ Внимание! Без `;` в конце строки будет ошибка
let bar;
// ...
( { foo, bar } = obj )

        
        
          
        
      

Деструктуризация массивов

Скопировано

При деструктуризации массива можете пропускать элементы, значения которых получать не нужно. Для пропуска элемента используют оператор ,:

        
          
          const animals = ['🐱', '🐴', '🦆', '🐶', '🐸', '🐹']const [cat, , , dog] = animals // Пропускаем '🐴', '🦆'console.log(cat, dog)// 🐱 🐶
          const animals = ['🐱', '🐴', '🦆', '🐶', '🐸', '🐹']

const [cat, , , dog] = animals // Пропускаем '🐴', '🦆'

console.log(cat, dog)
// 🐱 🐶

        
        
          
        
      

Можно пропускать один элемент, несколько элементов подряд или их групп:

        
          
          const animals = ['🐱', '🐴', '🦆', '🐶', '🐸', '🐹']const [, , duck, , frog] = animals // Пропускаем '🐱', '🐴', '🐶'console.log(duck, frog)// 🦆 🐸
          const animals = ['🐱', '🐴', '🦆', '🐶', '🐸', '🐹']

const [, , duck, , frog] = animals // Пропускаем '🐱', '🐴', '🐶'

console.log(duck, frog)
// 🦆 🐸

        
        
          
        
      

Можно предусмотреть значения по умолчанию. Понадобится, если массив пустой или в нём незаполненные элементы.

        
          
          const options = Array(5) // [ <5 empty items> ]const [first = 'Опция по умолчанию', second] = optionsconsole.log(first)// Опция по умолчаниюconsole.log(second)// undefined
          const options = Array(5) // [ <5 empty items> ]

const [first = 'Опция по умолчанию', second] = options

console.log(first)
// Опция по умолчанию

console.log(second)
// undefined

        
        
          
        
      

С помощью оператора rest (...) соберём в новый массив элементы, оставшиеся неизвлечёнными.

        
          
          const numbers = [2, 4, 8, 16, 32 ,64]const [elem0, , elem2, ...restNumbers] = numbersconsole.log(elem0, elem2)// 2 8console.log(second)// [ 16, 32, 64 ]
          const numbers = [2, 4, 8, 16, 32 ,64]

const [elem0, , elem2, ...restNumbers] = numbers

console.log(elem0, elem2)
// 2 8

console.log(second)
// [ 16, 32, 64 ]

        
        
          
        
      

Учитывайте, что rest-оператор указывают последним, иначе произойдёт ошибка:

        
          
          const numbers = [2, 4, 8, 16, 32 ,64]const [elem0, ...restNumbers, elemLast ] = numbers// SyntaxError: Rest element must be last element
          const numbers = [2, 4, 8, 16, 32 ,64]

const [elem0, ...restNumbers, elemLast ] = numbers
// SyntaxError: Rest element must be last element

        
        
          
        
      

Также можете произвести деструктуризацию в массиве, образуемом rest-оператором:

        
          
          const numbers = [2, 4, 8, 16, 32 ,64]const [elem0, elem1, ...[ elem2, ...restNumbers ] ] = numbersconsole.log(elem2)// 8console.log(restNumbers)// [16, 32, 64]
          const numbers = [2, 4, 8, 16, 32 ,64]

const [elem0, elem1, ...[ elem2, ...restNumbers ] ] = numbers

console.log(elem2)
// 8

console.log(restNumbers)
// [16, 32, 64]

        
        
          
        
      

С помощью деструктуризации удобно извлекать значения из вложенных массивов.

        
          
          const numbers = [  1,  [2, [2.3, 2.89]],  4]// Извлечём значения первого элемента массиваconst [, [elem10, [elem11, elem12]]] = numbersconsole.log(elem10, elem11, elem12)// 2 2.3 2.89
          const numbers = [
  1,
  [2, [2.3, 2.89]],
  4
]

// Извлечём значения первого элемента массива
const [, [elem10, [elem11, elem12]]] = numbers

console.log(elem10, elem11, elem12)
// 2 2.3 2.89

        
        
          
        
      

Использование деструктуризации

Скопировано

Синтаксис деструктуризации улучшает читаемость кода. Рассмотрим несколько примеров.

Передача множества параметров в функцию

Скопировано

Лучше избегать ситуаций, когда функция принимает множество параметров. При вызове такой функции легко запутаться в очерёдности аргументов, особенно если часть из них не обязательна:

        
          
          function createOption(title, hint="" , description="", callback, classes) {  // Реализация}// Пример вызоваconst loadFileOption = createOption(  'Открыть файл',  undefined,  undefined,  loadFileHandler,  'option')
          function createOption(title, hint="" , description="", callback, classes) {
  // Реализация
}

// Пример вызова
const loadFileOption = createOption(
  'Открыть файл',
  undefined,
  undefined,
  loadFileHandler,
  'option'
)

        
        
          
        
      

Можем улучшить этот код, заменив множества отдельных параметров на один объект. Тут как раз и пригодится деструктуризация. За счёт минимальных изменений получаем:

  • названия свойств передаваемого объекта помогают понять смысл параметра;
  • нет необходимости строгого соблюдения порядка аргументов при вызове.
        
          
          function createOption({title, hint="" , description="", callback, classes}) {  // Реализация}// Пример вызоваconst loadFileOption = createOption({  title: 'Открыть файл',  classes:'option'  callback: loadFileHandler})
          function createOption({title, hint="" , description="", callback, classes}) {
  // Реализация
}

// Пример вызова
const loadFileOption = createOption({
  title: 'Открыть файл',
  classes:'option'
  callback: loadFileHandler
})

        
        
          
        
      

Уточнение аргумента колбэка в методах массива

Скопировано

Допустим, мы хотим отфильтровать массив объектов на основе нескольких свойств:

        
          
          const images = [  {    name: 'Кот в сапогах',    data: {      type: 'book',      size: [800, 600]    }  },  {    name: 'Тётя-тётя Кошка',    data: {      type: 'book',      size: [640, 480]    },  },  {    name: 'Васька слушает, да ест',    data: {      type: 'mem',      size: [1024, 768]    }  },]// Получаем значения нужных свойств внутри колбэк-функцииconst bookImages = images.filter((item) => {  if (item.data.type === 'book' && item.data.size[0] > 640) {    return true  }  return false})
          const images = [
  {
    name: 'Кот в сапогах',
    data: {
      type: 'book',
      size: [800, 600]
    }
  },
  {
    name: 'Тётя-тётя Кошка',
    data: {
      type: 'book',
      size: [640, 480]
    },
  },
  {
    name: 'Васька слушает, да ест',
    data: {
      type: 'mem',
      size: [1024, 768]
    }
  },
]

// Получаем значения нужных свойств внутри колбэк-функции
const bookImages = images.filter((item) => {
  if (item.data.type === 'book' && item.data.size[0] > 640) {
    return true
  }
  return false
})

        
        
          
        
      

Деструктуризация поможет извлечь нужные свойства при передаче параметра в колбэк. Это упростит код функции:

        
          
          const bookImages = images.filter(({data:{type, size:[width]}}) => {  if (type === 'book' && width > 640) {    return true  }  return false})
          const bookImages = images.filter(({data:{type, size:[width]}}) => {
  if (type === 'book' && width > 640) {
    return true
  }
  return false
})

        
        
          
        
      

Получение из функции нескольких значений

Скопировано

Иногда удобно получать из функции несколько значений. Деструктуризация результата выполнения функции не создаст временный массив при получении:

        
          
          function getMinMax (numbers = []) {  const minValue = Math.min(...numbers)  const maxValue = Math.max(...numbers)  return [minValue, maxValue]}const [min, max] = getMinMax([2, 0, 10, 5, 8])console.log(min, max)// 0 10
          function getMinMax (numbers = []) {
  const minValue = Math.min(...numbers)
  const maxValue = Math.max(...numbers)
  return [minValue, maxValue]
}

const [min, max] = getMinMax([2, 0, 10, 5, 8])

console.log(min, max)
// 0 10

        
        
          
        
      

Трюки

Скопировано

Рассмотрим несколько неочевидных возможностей применения деструктуризации.

Обмен значений переменных одной операцией

Скопировано

Скорее всего, этого не потребуется в реальном проекте, но такая возможность есть.

        
          
          let a = 8// ⚠️ Внимание! Без `;` в конце строки будет ошибкаlet b = 16;[a, b] = [b, a]console.log(a, b)// 16 8
          let a = 8
// ⚠️ Внимание! Без `;` в конце строки будет ошибка
let b = 16;

[a, b] = [b, a]

console.log(a, b)
// 16 8

        
        
          
        
      

Копирование свойств в объект

Скопировано

При деструктуризации объекта извлекаемые значения можно напрямую помещать в другой, ранее созданный объект:

        
          
          function getDeviceParams () {  // Объект-приёмник  // ⚠️ Внимание! Без `;` в конце строки будет ошибка  const data = {lastCall: Date.now(), window: {}};  // Копируем нужные свойства из объекта window в объект data  ( {    screen: {      orientation: {type: data.orientation}      availWidth: data.window.availableWidth      availHeight: data.window.availableHeight    },    navigator: {language: data.lang}  } = window )  return data}console.log(getDeviceParams())// {//   lang: 'en-US',//   lastCall: 1726815477972,//   orientation: 'landscape-primary',//   window: { availableHeight: 1040, availableWidth: 1920 }// }
          function getDeviceParams () {
  // Объект-приёмник
  // ⚠️ Внимание! Без `;` в конце строки будет ошибка
  const data = {lastCall: Date.now(), window: {}};

  // Копируем нужные свойства из объекта window в объект data
  ( {
    screen: {
      orientation: {type: data.orientation}
      availWidth: data.window.availableWidth
      availHeight: data.window.availableHeight
    },
    navigator: {language: data.lang}
  } = window )

  return data
}

console.log(getDeviceParams())
// {
//   lang: 'en-US',
//   lastCall: 1726815477972,
//   orientation: 'landscape-primary',
//   window: { availableHeight: 1040, availableWidth: 1920 }
// }

        
        
          
        
      

Извлечение крайних элементов массива одной операцией

Скопировано

Так как массив — это также и объект, с помощью деструктуризации можем извлекать его свойства. Разберём решения по частям.

Элементы массива доступны как свойства с именами-индексами:

        
          
          const numbers = [5, 7, 9, 3]const {0: first, 1: second} = numbersconsole.log(first, second)// 5 7
          const numbers = [5, 7, 9, 3]

const {0: first, 1: second} = numbers

console.log(first, second)
// 5 7

        
        
          
        
      

Зная размер массива, получим доступ к последнему элементу:

        
          
          const { [numbers.length - 1]: last } = numbersconsole.log(last)// 3
          const { [numbers.length - 1]: last } = numbers

console.log(last)
// 3

        
        
          
        
      

А теперь объединим всё вместе:

        
          
          const numbers = [5, 7, 9, 3]const { 0: first, [numbers.length - 1]: last } = numbersconsole.log(first, last)// 5 3
          const numbers = [5, 7, 9, 3]

const { 0: first, [numbers.length - 1]: last } = numbers

console.log(first, last)
// 5 3