Двумя наиболее часто используемыми структурами данных в JavaScript являются Object
и Array
.
Объекты позволяют нам создавать единую сущность, которая хранит элементы данных по ключу.
Массивы позволяют нам собирать элементы данных в упорядоченный список.
Однако когда мы передаем их функции, нам может не понадобиться все это. Для функции могут потребоваться только определенные элементы или свойства.
Деструктуризация присваивания — это специальный синтаксис, который позволяет нам «распаковывать» массивы или объекты в набор переменных, поскольку иногда это удобнее.
Деструктуризация также хорошо работает со сложными функциями, имеющими множество параметров, значений по умолчанию и т. д. Скоро мы это увидим.
Вот пример того, как массив разбивается на переменные:
// у нас есть массив с именем и фамилией let arr = ["Джон", "Смит"] // деструктуризация присваивания // устанавливает firstName = arr[0] // и фамилия = arr[1] пусть [имя, фамилия] = обр; оповещение (первое имя); // Джон оповещение (фамилия); // Смит
Теперь мы можем работать с переменными вместо членов массива.
Он отлично смотрится в сочетании с split
или другими методами возврата массива:
let [firstName, surname] = "Джон Смит".split(' '); оповещение (первое имя); // Джон оповещение (фамилия); // Смит
Как видите, синтаксис прост. Однако есть несколько любопытных деталей. Давайте посмотрим больше примеров, чтобы лучше понять это.
«Деструктуризация» не означает «разрушение».
Это называется «деструктурирующим присваиванием», потому что оно «деструктуризует» путем копирования элементов в переменные. Однако сам массив не изменяется.
Это просто более короткий способ записи:
// let [имя, фамилия] = arr; пусть firstName = arr[0]; пусть фамилия = arr[1];
Игнорировать элементы, использующие запятые
Ненужные элементы массива также можно отбросить с помощью лишней запятой:
// второй элемент не нужен let [firstName, , title] = ["Юлий", "Цезарь", "Консул", "Римской республики"]; предупреждение (заголовок); // Консул
В приведенном выше коде пропускается второй элемент массива, третий присваивается title
, а также остальные элементы массива (поскольку для них нет переменных).
Работает с любой итерацией справа.
…На самом деле, мы можем использовать его с любыми итерируемыми объектами, а не только с массивами:
пусть [a, b, c] = «abc»; // ["а", "б", "в"] пусть [один, два, три] = новый Set([1, 2, 3]);
Это работает, потому что внутри присваивание деструктуризации работает путем перебора правильного значения. Это своего рода синтаксический сахар для вызова for..of
значения справа от =
и присвоения значений.
Назначьте что-нибудь слева
Мы можем использовать любые «назначаемые объекты» с левой стороны.
Например, свойство объекта:
пусть пользователь = {}; [user.name, user.surname] = "Джон Смит".split(' '); оповещение(имя_пользователя); // Джон оповещение(пользователь.фамилия); // Смит
Цикл с помощью .entries()
В предыдущей главе мы видели метод Object.entries(obj).
Мы можем использовать его с деструктуризацией для перебора ключей и значений объекта:
пусть пользователь = { имя: «Джон», возраст: 30 }; // цикл по ключам и значениям for (let [ключ, значение] Object.entries(user)) { alert(`${key}:${value}`); // имя: Джон, затем возраст: 30 }
Аналогичный код для Map
проще, поскольку он повторяемый:
пусть пользователь = новая карта(); user.set("имя", "Джон"); user.set("возраст", "30"); // Карта повторяется как пары [ключ, значение], что очень удобно для деструктуризации for (let [ключ, значение] пользователя) { alert(`${key}:${value}`); // имя: Джон, затем возраст: 30 }
Трюк с заменой переменных
Есть известный трюк для замены значений двух переменных с помощью деструктурирующего присваивания:
пусть гость = "Джейн"; let admin = "Пит"; // Давайте поменяем значения: make Guest=Pete, admin=Jane [гость, администратор] = [администратор, гость]; alert(`${guest} ${admin}`); // Пит Джейн (успешно заменен!)
Здесь мы создаем временный массив из двух переменных и сразу деструктурируем его в порядке замены.
Таким образом мы можем поменять местами более двух переменных.
Обычно, если массив длиннее списка слева, «лишние» элементы опускаются.
Например, здесь берутся только два элемента, а остальные просто игнорируются:
let [имя1, имя2] = ["Юлий", "Цезарь", "Консул", "Римской республики"]; предупреждение (имя1); // Юлий оповещение (имя2); // Цезарь // Дальнейшие элементы никуда не назначаются
Если мы хотим также собрать все, что следует, мы можем добавить еще один параметр, который получает «остальное», используя три точки "..."
:
let [имя1, имя2, ...rest] = ["Юлий", "Цезарь", "Консул", "Римской республики"]; // rest — массив элементов, начиная с третьего оповещение (остальное [0]); // Консул оповещение (остальное [1]); // Римской республики оповещение(rest.length); // 2
Значение rest
— это массив оставшихся элементов массива.
Вместо rest
мы можем использовать любое другое имя переменной, просто убедитесь, что перед ним стоят три точки и оно идет последним в присваивании деструктуризации.
let [name1, name2, ...titles] = ["Юлий", "Цезарь", "Консул", "Римской республики"]; // теперь titles = ["Консул", "Римской республики"]
Если массив короче списка переменных слева, ошибок не будет. Отсутствующие значения считаются неопределенными:
пусть [имя, фамилия] = []; оповещение (первое имя); // неопределенный оповещение (фамилия); // неопределенный
Если мы хотим, чтобы значение «по умолчанию» заменило отсутствующее, мы можем предоставить его, используя =
:
// значения по умолчанию let [имя = "Гость", фамилия = "Аноним"] = ["Юлий"]; оповещение (имя); // Юлий (из массива) оповещение (фамилия); // Анонимно (используется по умолчанию)
Значениями по умолчанию могут быть более сложные выражения или даже вызовы функций. Они оцениваются только в том случае, если значение не указано.
Например, здесь мы используем функцию prompt
для двух значений по умолчанию:
// запускает только запрос на ввод фамилии let [имя = приглашение('имя?'), фамилия = приглашение('фамилия?')] = ["Юлий"]; оповещение (имя); // Юлий (из массива) оповещение (фамилия); // какое бы приглашение ни пришло
Обратите внимание: prompt
будет запущен только для отсутствующего значения ( surname
).
Присвоение деструктуризации также работает с объектами.
Основной синтаксис:
пусть {var1, var2} = {var1:…, var2:…}
Справа у нас должен быть существующий объект, который мы хотим разделить на переменные. Левая часть содержит объектный «шаблон» для соответствующих свойств. В простейшем случае это список имен переменных в {...}
.
Например:
пусть варианты = { заголовок: «Меню», ширина: 100, высота: 200 }; let {title, width, height} = options; предупреждение (название); // Меню предупреждение (ширина); // 100 оповещение (высота); // 200
Свойства options.title
, options.width
и options.height
присваиваются соответствующим переменным.
Порядок не имеет значения. Это тоже работает:
// изменил порядок в let {...} let {height, width, title} = { title: "Меню", высота: 200, ширина: 100 }
Шаблон слева может быть более сложным и указывать сопоставление свойств и переменных.
Если мы хотим присвоить свойство переменной с другим именем, например, заставить options.width
перейти в переменную с именем w
, тогда мы можем установить имя переменной, используя двоеточие:
пусть варианты = { заголовок: «Меню», ширина: 100, высота: 200 }; // { sourceProperty: targetVariable } let {ширина: ш, высота: ч, заголовок} = параметры; // ширина -> ш // высота -> ч // заголовок -> заголовок предупреждение (название); // Меню оповещение (ш); // 100 предупреждение(ч); // 200
Двоеточие показывает «что: куда идет». В приведенном выше примере width
свойства переходит к w
, height
свойства — к h
, а title
присваивается то же имя.
Для потенциально отсутствующих свойств мы можем установить значения по умолчанию, используя "="
, например:
пусть варианты = { заголовок: «Меню» }; пусть {ширина = 100, высота = 200, заголовок} = параметры; предупреждение (название); // Меню предупреждение (ширина); // 100 оповещение (высота); // 200
Как и в случае с массивами или параметрами функций, значениями по умолчанию могут быть любые выражения или даже вызовы функций. Они будут оценены, если значение не указано.
В приведенном ниже коде prompt
запрашивает width
, но не title
:
пусть варианты = { заголовок: «Меню» }; let {ширина = приглашение («ширина?»), заголовок = приглашение («название?»)} = параметры; предупреждение (название); // Меню предупреждение (ширина); // (каким бы ни был результат приглашения)
Мы также можем объединить двоеточие и равенство:
пусть варианты = { заголовок: «Меню» }; let {ширина: ш = 100, высота: ч = 200, заголовок} = параметры; предупреждение (название); // Меню оповещение (ш); // 100 предупреждение(ч); // 200
Если у нас сложный объект со множеством свойств, мы можем извлечь только то, что нам нужно:
пусть варианты = { заголовок: «Меню», ширина: 100, высота: 200 }; // извлекаем только заголовок как переменную let {title} = варианты; предупреждение (название); // Меню
Что, если у объекта больше свойств, чем переменных? Можем ли мы взять часть, а затем куда-то присвоить «остальное»?
Мы можем использовать шаблон rest, как и в случае с массивами. Он не поддерживается некоторыми старыми браузерами (IE, используйте Babel для его заполнения), но работает в современных.
Это выглядит так:
пусть варианты = { заголовок: «Меню», высота: 200, ширина: 100 }; // title = свойство с именем title // rest = объект с остальными свойствами let {title, ...rest} = параметры; // теперь title="Меню", rest={высота: 200, ширина: 100} оповещение(rest.height); // 200 оповещение (rest.width); // 100
Попался, если нет let
В приведенных выше примерах переменные были объявлены прямо в присваивании: let {…} = {…}
. Конечно, мы могли бы использовать и существующие переменные, без let
. Но есть одна загвоздка.
Это не сработает:
пусть заголовок, ширина, высота; // ошибка в этой строке {title, width, height} = {title: "Меню", ширина: 200, высота: 100};
Проблема в том, что JavaScript обрабатывает {...}
в основном потоке кода (а не внутри другого выражения) как блок кода. Такие блоки кода можно использовать для группировки операторов, например:
{ // блок кода let message = «Привет»; // ... предупреждение(сообщение); }
Итак, здесь JavaScript предполагает, что у нас есть блок кода, поэтому возникает ошибка. Вместо этого мы хотим деструктуризации.
Чтобы показать JavaScript, что это не блок кода, мы можем заключить выражение в круглые скобки (...)
:
пусть заголовок, ширина, высота; // окей, теперь ({title, width, height} = {title: "Меню", ширина: 200, высота: 100}); предупреждение (заголовок); // Меню
Если объект или массив содержит другие вложенные объекты и массивы, мы можем использовать более сложные левые шаблоны для извлечения более глубоких частей.
В приведенном ниже коде options
имеют еще один объект в свойстве size
и массив в свойствах items
. Шаблон в левой части присваивания имеет ту же структуру для извлечения из них значений:
пусть варианты = { размер: { ширина: 100, высота: 200 }, предметы: ["Торт", "Пончик"], дополнительно: правда }; // деструктуризация присваивания разбивается на несколько строк для ясности позволять { size: { // указываем здесь размер ширина, высота }, items: [item1, item2], // здесь назначаем элементы title = "Меню" // отсутствует в объекте (используется значение по умолчанию) } = варианты; предупреждение (название); // Меню предупреждение (ширина); // 100 оповещение (высота); // 200 предупреждение (пункт1); // Торт предупреждение (пункт2); // Пончик
Все свойства объекта options
, кроме extra
, отсутствующих в левой части, присваиваются соответствующим переменным:
Наконец, у нас есть width
, height
, item1
, item2
и title
из значения по умолчанию.
Обратите внимание, что здесь нет переменных для size
и items
, поскольку вместо этого мы берём их содержимое.
Бывают случаи, когда функция имеет много параметров, большинство из которых являются необязательными. Это особенно верно для пользовательских интерфейсов. Представьте себе функцию, которая создает меню. Он может иметь ширину, высоту, заголовок, список элементов и т. д.
Вот плохой способ написать такую функцию:
function showMenu(title = "Без названия", ширина = 200, высота = 100, элементы = []) { // ... }
В реальной жизни проблема заключается в том, как запомнить порядок аргументов. Обычно IDE пытаются нам помочь, особенно если код хорошо документирован, но всё же… Другая проблема — как вызвать функцию, когда большинство параметров по умолчанию в порядке.
Так?
// не определено, где значения по умолчанию подходят showMenu("Мое меню", не определено, не определено, ["Пункт1", "Пункт2"])
Это некрасиво. И становится нечитаемым, когда мы имеем дело с большим количеством параметров.
На помощь приходит деструктуризация!
Мы можем передавать параметры как объект, и функция сразу деструктурирует их в переменные:
// передаем объект в функцию пусть варианты = { title: "Мое меню", предметы: ["Предмет1", "Предмет2"] }; // ...и он сразу же расширяет его до переменных function showMenu({title = "Без названия", ширина = 200, высота = 100, элементы = []}) { // заголовок, элементы – взяты из опций, // ширина, высота – используются значения по умолчанию alert( `${title} ${width} ${height}` ); // Мое меню 200 100 оповещение(элементы); // Товар1, Товар2 } показатьМеню (опции);
Мы также можем использовать более сложную деструктуризацию с вложенными объектами и сопоставлениями двоеточий:
пусть варианты = { title: "Мое меню", предметы: ["Предмет1", "Предмет2"] }; функция showMenu({ заголовок = "Без названия", ширина: w = 100, // ширина переходит в w height: h = 200, // высота равна h items: [item1, item2] // первый элемент элемента идет к item1, второй — к item2 }) { alert( `${title} ${w} ${h}` ); // Мое меню 100 200 предупреждение(элемент1); // Товар1 предупреждение(элемент2); // Элемент2 } показатьМеню (опции);
Полный синтаксис такой же, как и для деструктурирующего присваивания:
функция({ входящеесвойство: varName = значение по умолчанию ... })
Тогда для объекта параметров будет переменная varName
для свойства incomingProperty
со defaultValue
по умолчанию.
Обратите внимание: такая деструктуризация предполагает, что у showMenu()
есть аргумент. Если нам нужны все значения по умолчанию, нам следует указать пустой объект:
шоуМеню({}); // ок, все значения по умолчанию показатьМеню(); // это приведет к ошибке
Мы можем это исправить, сделав {}
значением по умолчанию для всего объекта параметров:
function showMenu({ title = "Меню", ширина = 100, высота = 200 } = {}) { alert( `${title} ${width} ${height}` ); } показатьМеню(); // Меню 100 200
В приведенном выше коде весь объект аргументов по умолчанию имеет вид {}
, поэтому всегда есть что деструктуризировать.
Деструктуризация присваивания позволяет мгновенно сопоставить объект или массив со многими переменными.
Полный синтаксис объекта:
let {prop: varName = defaultValue, ...rest} = объект
Это означает, что свойство prop
должно быть помещено в переменную varName
, и, если такого свойства не существует, следует использовать значение default
.
Свойства объекта, не имеющие сопоставления, копируются в rest
объект.
Полный синтаксис массива:
let [item1 = defaultValue, item2, ...rest] = массив
Первый элемент переходит в item1
; второй переходит в item2
, а все остальное приводит в rest
массив.
Из вложенных массивов/объектов можно извлекать данные, для этого левая часть должна иметь ту же структуру, что и правая.
важность: 5
У нас есть объект:
пусть пользователь = { имя: «Джон», лет: 30 };
Напишите деструктурирующее задание следующего содержания:
свойство name
в name
переменной.
years
собственности в переменный age
.
isAdmin
в переменную isAdmin
(false, если такого свойства нет)
Вот пример значений после вашего назначения:
let user = { name: "Джон", лет: 30 }; // ваш код слева: // ... = пользователь оповещение (имя); // Джон предупреждение (возраст); // 30 оповещение (isAdmin); // ЛОЖЬ
пусть пользователь = { имя: «Джон», лет: 30 }; пусть {имя, годы: возраст, isAdmin = false} = пользователь; оповещение (имя); // Джон предупреждение (возраст); // 30 оповещение (isAdmin); // ЛОЖЬ
важность: 5
Есть объект salaries
:
пусть зарплаты = { «Джон»: 100, «Пит»: 300, «Мария»: 250 };
Создайте функцию topSalary(salaries)
, которая возвращает имя самого высокооплачиваемого человека.
Если salaries
пусто, оно должно вернуть null
.
Если имеется несколько высокооплачиваемых лиц, верните любого из них.
PS Используйте Object.entries
и деструктуризацию для перебора пар ключ/значение.
Откройте песочницу с тестами.
функция topSalary(зарплаты) { пусть maxSalary = 0; пусть maxName = null; for(const [имя, зарплата] of Object.entries(salaries)) { if (maxSalary <зарплата) { maxSalary = зарплата; МаксИмя = имя; } } вернуть максимальное имя; }
Откройте решение с тестами в песочнице.