Деструктуризация — очень мощный инструмент в 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.