Деструктуризация — очень мощный инструмент в JavaScript. С помощью деструктурирующего присваивания мы можем распаковать значения элементов из массива или свойства объекта и сохранить их как отдельные переменные для удобства использования в нашей программе.

Основная структура

Переменные, в которых хранятся значения или свойства, заключены с синтаксисом деструктурирования, который является либо литералом массива, т. е. […], для массив или литерал объекта, т. е. {…}, для объекта, и помещаются слева от оператора присваивания (т. е. , =). Справа размещаем массив или объект, который нужно деструктурировать.

// the array and the object to be destructured.
const myArray = [10, 20, 30];
const myObject = { d: 40, e: 50, f: 60 };

// destructring assignments 
const [a, b, c] = myArray;
const {d, e, f} = myObject;

// outputting destructured values 
console.log(a, b, c); // → 10 20 30
console.log(d, e, f); // → 40 50 60  

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

// destructring the array and the object directly 
const [a, b, c] = [10, 20, 30];
const {d, e, f} = { d: 40, e: 50, f: 60 };

// outputting destructured values 
console.log(a, b, c); // → 10 20 30
console.log(d, e, f); // → 40 50 60

Помните о порядке и переменных, хранящих деструктурированные значения

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

const myArray = [1, 2, 3];

// use any variable name of your choice to store the destructured values
const [tom, dick, harry] = myArray;
console.log(tom, dick, harry); // → 1 2 3

// the variable names should be valid variable names 
const [one, two, three] = myArray; // this is ok
const [1, 2, 3] = myArray; // throws error, invalid names 1, 2, 3

// order is important in array destructuring
const [first, third, second] = myArray;
console.log(first, second, third); // → 1 3 2

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

const myObject = { d: 10, e: 20, f: 30 };

// the property names must match when you store the destructured values
const { d, e, f } = myObject; // this is ok
const { dog, elephant, fish } = myObject; // this can not store the values

// order is not important in object destructuring
const anotherObject = { first: 10, second: 20, third: 30 };
const { first, third, second } = anotherObject;
console.log(first, second, third); // → 10 20 30

Работа с необычными именами свойств в объекте

Имя свойства объекта JavaScript может быть допустимым именем привязки (имя переменной), числом или даже любой строкой. Таким образом, у вас может возникнуть путаница, когда вам нужно деструктурировать объект с такими необычными именами. Но имейте в виду, что любое имя свойства объекта является строковым значением.

const myObject = {
  one: 1,
  2: 2,
  "property 3": 3,
  "number-4": 4
};

console.log(Object.keys(myObject)); 
// → ["2", "one", "property 3", "number-4"]

Когда это строковое значение может быть допустимым именем переменной, мы можем использовать его напрямую. И когда это недопустимое имя переменной (например, 2), мы можем использовать строковое значение для ссылки на свойство объекта, но затем мы можем назначить это значение свойства с помощью ': ', за которым следует любая переменная с именем по нашему выбору — это имя может отличаться от имени свойства объекта, но должно быть допустимым именем переменной (например, два).

const {one, "2":two} = myObject; 
console.log(one, two); // → 1 2

Мы также можем использовать нотацию с квадратными скобками, т. е. […] для представления вычисленного значения — имени свойства объекта.

const key3 = "property 3";
const { [key3]:three, ["number"+"-"+"4"]:four } = myObject;
console.log(three, four); // → 3 4

Разрушение некоторых, всех и других ценностей; и значения по умолчанию

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

const myArray = [1, 2, 3, 4];
const [one, two]  = myArray;
console.log(one, two); // → 1 2

Но поскольку порядок важен при деструктурировании массива, что мы делаем, когда хотим выбрать любые два значения из приведенного выше массива, а не обязательно первые два? Идея проста. При сохранении порядка вы можете игнорировать любое из значений, оставив пустое место после запятой (например: значение1, , значение3).

const myArray = [1, 2, 3, 4];
const [ ,two, ,four ] = myArray; // skipping the first and third elements
console.log(two, four); // → 2 4

Иногда мы можем не знать точно, сколько элементов содержится в массиве (например, когда мы получаем этот массив как возвращаемое значение из функции). Мы можем выполнить присваивание деструктуризации массива большему количеству переменных, чем элементов в данном массиве. Затем эти дополнительные значения будут созданы и инициализированы неопределенными значениями, как показано ниже:

const myArray = [1, 2, 3 ];
const [a, b, c, d, e] = myArray;
console.log(a, b, c, d, e); // → 1 2 3 undefined undefined

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

const myArray = [1, 2, 3 ];
const [a = 0, b = 0, c = 0, d = 0, e = 0] = myArray;
console.log(a, b, c, d, e); // → 1 2 3 0 0

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

const myObject = { a: 1, b: 2, c: 3 };
const { a, b, c, d, e } = myObject;
console.log(a, b, c, d, e); // → 1 2 3 undefined undefined

const newObject = { one: 1, two: 2, three: 3 };
const { one = 0, two = 0, three = 0, four = 0, five = 0 } = newObject;
console.log (one, two, three, four, five); // → 1 2 3 0 0

Деструктурирование присвоения существующим переменным

Теперь давайте обсудим некоторые другие практические варианты использования. Сначала рассмотрим случай, когда мы не создаем новые переменные для хранения деструктурированных значений. Вместо этого у нас есть существующие переменные, которым нужно переназначить эти новые значения. Можем ли мы сделать что-то вроде следующего?

Что ж, для массивов это нормально, как вы можете видеть в следующем примере:

let a = 1, b = 2;
const myArray = [10, 20, 30];
[a, b, c] = myArray;
console.log(a, b, c); // → 10 20 30

В строке № 3 выше мы не можем снова использовать let, потому что a и b уже объявлены с помощью let. И любое объявление переменной с помощью let не допускает повторного объявления. Следовательно, это утверждение: «пусть [a, b, c] = myArray;» выдаст ошибку. Но их можно переназначать столько раз, сколько нам нужно. Следовательно, «[a, b, c] = myArray;» работает отлично. Это случай переназначения уже объявленных переменных. Если бы в строке №1 мы объявили переменные с помощью ключевого слова var, мы, конечно, могли бы повторно объявить их в строке №3 — но это просто потому, что var поддерживает повторное объявление, а let — нет. Мы игнорируем этот вариант использования с «var», потому что это создало бы новый набор переменных, а не переназначение — что здесь и является целью.

Также обратите внимание, что даже переменная «c», которая не была объявлена ​​ранее, создается и получает значение. Это связано с тем, что JavaScript может создать переменную, даже если мы не указываем никаких ключевых слов var, let и const, и эта переменная назначается глобальной области видимости. И поэтому следующий код допустим в JavaScript (хотя и не рекомендуется).

x = 13;
console.log(x); // → 13

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

let a = 1, b = 2;
const myObject = { a: 10, b: 20, c: 30 };
{ a, b, c } = myObject;
// → SyntaxError: Unexpected token '='

Это связано с тем, что в строке № 3 выше JavaScript считает часть в фигурных скобках, то есть {_}, блоком кода и не может ничего ему присвоить. И, как обсуждалось ранее, мы не можем использовать let в начале строки, так как повторное объявление переменных на основе let не допускается. Итак, как с этим бороться?

Что ж, ответ приводит нас к новой теме — есть два способа деструктурировать массив или объект, как описано в следующем разделе.

Два шаблона разрушения массива или объекта

До сих пор мы создавали некоторые переменные (также называемые привязками) с ключевым словом (например, var, let или const), а затем к этим переменным мы привязывали (сохраняли) деструктурированные значения. Этот шаблон называется шаблон привязки. Другой способ деструктурирования называется шаблон присваивания, в котором синтаксис немного отличается: этот шаблон не начинается с ключевого слова. Вместо этого каждое деструктурированное свойство присваивается цели присваивания, которую можно объявить заранее с помощью var или let (но не const, потому что const нельзя объявить заранее без какого-либо присваивания, и они не могут быть переназначены, если им было присвоено какое-то значение ранее). Если приведенное выше описание недостаточно ясно, следующие четыре примера, мы надеемся, продемонстрируют два шаблона для массивов и объектов.

// Ex 1: Array Destructuring in Binding Pattern
const myArray = [1, 2, 3];
const [one, two, three] = myArray;
console.log(one, two, three); // → 1 2 3

// Ex 2: Array Destructuring in Assignment Pattern
const otherArray = [4, 5, 6];
let four, five, six;
([four, five, six] = otherArray);
console.log(four, five, six); // → 4 5 6

// Ex 3: Object Destructuring in Binding Pattern
const myObject = { a: 1, b: 2, c: 3 };
const {a, b, c} = myObject;
console.log(a, b, c); // → 1 2 3

// Ex 4: Array Destructuring in Assignment Pattern
const otherObject = { d: 4, e: 5, f: 6 };
let d, e, f;
({d, e, f} = otherObject);
console.log(d, e, f); // → 4 5 6

Кроме того, как вы можете видеть, последний пример выше (т. е. пример 4) является решением проблемы, с которой мы столкнулись в конце предыдущего раздела. Поэтому:

let a = 1, b = 2;
const myObject = { a: 10, b: 20, c: 30 };
{ a, b, c } = myObject;   // → SyntaxError: Unexpected token '='
({ a, b, c } = myObject); // → OK. A workaround for reassignment cases.

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

Подобно тому, как мы сохраняем оставшиеся аргументы в остальных параметрах вызова функции, в шаблоне деструктурирования мы можем использовать переменную со свойством rest (… за которым следует имя переменной, например …rest) для хранения все оставшиеся свойства объекта или оставшиеся элементы массива в новый объект или массив. Обратите внимание, что, в отличие от функций, при деструктурировании свойство rest должно быть последним в шаблоне деструктурирования, и после свойства rest не должно быть завершающей запятой.

const myArray = [1, 2, 3, 4, 5];
const myObject = {a: 1, b: 2, c: 3, d: 4, e: 5};

const [ one, two, ...restElements ] = myArray;
const { a, b, ...restProperties } = myObject;

console.log(one, two, restElements); // → 1 2 [3, 4, 5]
console.log(a, b, restProperties); // → 1 2 {c: 3, d: 4, e: 5}

Замена переменных с деструктурированием

Как показано ниже, два значения переменных можно поменять местами с помощью деструктурирующего присваивания. Обратите внимание, что способ, который не использует деструктурирование, длиннее (использует три оператора/строки вместо одной) и требует использования временной переменной.

let a = 1, b = 2;
console.log(a, b); // → 1 2

// swapping values of a and b without using destructuring
let temp = a;
a = b;
b = temp;
console.log(a, b); // → 2 1

let x = 10, y = 20;
console.log(x, y); // → 10 20

// swapping values of x and y using destructuring
[ x, y ] = [ y, x ];
console.log(x, y); // → 20 10

Доступ к нескольким значениям, возвращаемым функцией

Рассмотрим следующую функцию, которая возвращает несколько значений. Функция topScorers() возвращает заданное количество имен ранжировщиков в массиве. Например, если вы укажете значение 3 для параметра рангов, возвращаемое значение будет массивом имен трех лучших игроков.

const students = [
  { id: 1001, name: "Adam", score: 79 },
  { id: 1002, name: "Brian", score: 82 },
  { id: 1003, name: "Cynthia", score: 75 },
  { id: 1004, name: "Dave", score: 91 },
  { id: 1005, name: "Elly", score: 80 },
  { id: 1006, name: "Felicity", score: 72} 
];

function topScorers(studentsArray, ranks) {
   let sortedArray = studentsArray.sort(function(a, b) {
      return b.score - a.score;
   });

   let rankersArray = sortedArray.slice(0, ranks);

   function listing(details, property) {
     let list = [];
       for (let each of details) {
         list.push(each[property]);
        }
       return list;
   } 

   let rankersNames = listing(rankersArray, "name");

   return rankersNames;
}

topScorers(students, 3); // → ["Dave", "Brian", "Elly"]

Функция может вернуть только одно значение при вызове функции. Таким образом, если бы проблема заключалась в том, чтобы получить только первое место, мы могли бы сделать следующее с помощью вызова функции.

let firstRanker = topScorers(students, 1)[0];
console.log(firstRanker); // → Dave

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

let topThreeRankers = topScorers(students, 3);
let ranker1 = topThreeRankers[0];
let ranker2 = topThreeRankers[1];
let ranker3 = topThreeRankers[2];
console.log(ranker1, ranker2, ranker3); // → Dave Brian Elly

Но мы можем проанализировать эти несколько значений, возвращаемых функцией, в одном единственном присваивании деструктуризации, например:

let [ ranker1, ranker2, ranker3 ] = topScorers(students, 3);
console.log(ranker1, ranker2, ranker3); // → Dave Brian Elly

Доступ к нескольким значениям, переданным в функцию

В предыдущем разделе мы видели, как можно реструктурировать возвращаемые значения из функции. Здесь мы видим обратное: как функция может работать с деструктурированными значениями из переданного аргумента — в виде массива или объекта.

let currentWeatherData = {
  location: "New York",
  date: "2019-04-06",
  time: "08:05:37",
  condition: "cloudy",
  unitOfTemparature: "F",  
  temparature: 57,
  highestTempOfTheDay: 74,
  lowestTempOfTheDay: 49,
  unitOfWindspeed: "miles/hr",   
  windspeed: 12,
  unitOfChancesOfRain: "%",
  chancesOfRain: 10
};

function weatherBrief({condition, temparature, unitOfTemparature: unit}){
  const brief = 
  `Weather is ${condition} | Temparature: ${temparature} deg${unit}.`;
  return brief;
}

let weatherSummary = weatherBrief(currentWeatherData);
console.log(weatherSummary); // → Weather is cloudy | Temparature: 57 degF.

Вложенная деструктуризация и объединение объектов и массивов

Наконец, можно выполнить вложенную деструктуризацию, когда несколько объектов вложены друг в друга. Точно так же массивы и объекты могут быть объединены на этих вложенных уровнях. Мы можем деструктурировать до желаемой детализации, если будем следовать отдельным правилам деструктуризации массива (т. е. должен поддерживаться порядок значений) и деструктуризации объектов (т. е. имена свойств должны точно совпадать с именами переменных), как показано в следующем пример.

const countries = [
 {
  id: 1, 
  details: {name: "England", continent: "Europe", capital: "London"}
 },
 {
  id: 2, 
  details: {name: "Canada", continent: "N. America", capital: "Ottawa"}
 },
 {
  id: 3, 
  details: {name: "China", continent: "Asia", capital: "Beijing"}
 },
 {
  id: 4, 
  details: {name: "Australia", continent: "Oceania", capital: "Canberra"}
 }
]; 

const [,,{details: {name: myCountry, capital: myCapital}}] = countries;

console.log(myCountry, myCapital); // → China Beijing

Обратите внимание, что этот тип вложенной комбинации деструктуризации очень полезен при анализе нужной информации из результата вызова API, который может прийти с деревом данных. Тем не менее, только некоторые из этих деталей могут оказаться полезными для вашего приложения. Weather API от Open Weather — один из таких реальных примеров.

В этой статье обсуждались и демонстрировались многие варианты использования деструктуризации массивов и объектов. Подобные примеры, несомненно, повысят эффективность нашего кодирования. Тем не менее, пожалуйста, обратитесь к документам MDN для полного ознакомления с деструктуризацией JavaScript.