Деструктуризация (destructuring assignment) – это особый синтаксис присваивания, при котором можно присвоить массив или объект сразу нескольким переменным, разбив его на части.
Массив
Пример деструктуризации массива:
'use strict';
let [firstName, lastName] = ["Илья", "Кантор"];
alert(firstName); // Илья
alert(lastName); // Кантор
При таком присвоении первое значение массива пойдёт в переменную firstName
, второе – в lastName
, а последующие (если есть) – будут отброшены.
Ненужные элементы массива также можно отбросить, поставив лишнюю запятую:
'use strict';
// первый и второй элементы не нужны
let [, , title] = "Юлий Цезарь Император Рима".split(" ");
alert(title); // Император
В коде выше первый и второй элементы массива никуда не записались, они были отброшены. Как, впрочем, и все элементы после третьего.
Оператор «spread»
Если мы хотим получить и последующие значения массива, но не уверены в их числе – можно добавить ещё один параметр, который получит «всё остальное», при помощи оператора "..."
(«spread», троеточие):
'use strict';
let [firstName, lastName, ...rest] = "Юлий Цезарь Император Рима".split(" ");
alert(firstName); // Юлий
alert(lastName); // Цезарь
alert(rest); // Император,Рима (массив из 2х элементов)
Значением rest
будет массив из оставшихся элементов массива. Вместо rest
можно использовать и другое имя переменной, оператор здесь – троеточие. Оно должно стоять только последним элементом в списке слева.
Значения по умолчанию
Если значений в массиве меньше, чем переменных – ошибки не будет, просто присвоится undefined
:
'use strict';
let [firstName, lastName] = [];
alert(firstName); // undefined
Впрочем, как правило, в таких случаях задают значение по умолчанию. Для этого нужно после переменной использовать символ =
со значением, например:
'use strict';
// значения по умолчанию
let [firstName="Гость", lastName="Анонимный"] = [];
alert(firstName); // Гость
alert(lastName); // Анонимный
В качестве значений по умолчанию можно использовать не только примитивы, но и выражения, даже включающие в себя вызовы функций:
'use strict';
function defaultLastName() {
return Date.now() + '-visitor';
}
// lastName получит значение, соответствующее текущей дате:
let [firstName, lastName=defaultLastName()] = ["Вася"];
alert(firstName); // Вася
alert(lastName); // 1436...-visitor
Заметим, что вызов функции defaultLastName()
для генерации значения по умолчанию будет осуществлён только при необходимости, то есть если значения нет в массиве.
Деструктуризация объекта
Деструктуризацию можно использовать и с объектами. При этом мы указываем, какие свойства в какие переменные должны «идти».
Базовый синтаксис:
let {var1, var2} = {var1: …, var2: …};
Объект справа – уже существующий, который мы хотим разбить на переменные. А слева – список переменных, в которые нужно соответствующие свойства записать.
Например:
'use strict';
let options = {
title: "Меню",
width: 100,
height: 200
};
let {title, width, height} = options;
alert(title); // Меню
alert(width); // 100
alert(height); // 200
Как видно, свойства options.title
, options.width
и options.height
автоматически присвоились соответствующим переменным.
Если хочется присвоить свойство объекта в переменную с другим именем, например, чтобы свойство options.width
пошло в переменную w
, то можно указать соответствие через двоеточие, вот так:
'use strict';
let options = {
title: "Меню",
width: 100,
height: 200
};
let {width: w, height: h, title} = options;
alert(title); // Меню
alert(w); // 100
alert(h); // 200
В примере выше свойство width
отправилось в переменную w
, свойство height
– в переменную h
, а title
– в переменную с тем же названием.
Если каких-то свойств в объекте нет, можно указать значение по умолчанию через знак равенства =
, вот так;
'use strict';
let options = {
title: "Меню"
};
let {width=100, height=200, title} = options;
alert(title); // Меню
alert(width); // 100
alert(height); // 200
Можно и сочетать одновременно двоеточие и равенство:
'use strict';
let options = {
title: "Меню"
};
let {width:w=100, height:h=200, title} = options;
alert(title); // Меню
alert(w); // 100
alert(h); // 200
А что, если в объекте больше значений, чем переменных? Можно ли куда-то присвоить «остаток», аналогично массивам?
Такой возможности в текущем стандарте нет. Она планируется в будущем стандарте, и выглядеть она будет примерно так:
'use strict';
let options = {
title: "Меню",
width: 100,
height: 200
};
let {title, ...size} = options;
// title = "Меню"
// size = { width: 100, height: 200} (остаток)
Этот код будет работать, например, при использовании Babel со включёнными экспериментальными возможностями, но ещё раз заметим, что в текущий стандарт такая возможность не вошла.
В примерах выше переменные объявлялись прямо перед присваиванием: let {…} = {…}
. Конечно, можно и без let
, использовать уже существующие переменные.
Однако, здесь есть небольшой «подвох». В JavaScript, если в основном потоке кода (не внутри другого выражения) встречается {...}
, то это воспринимается как блок.
Например, можно использовать такой блок для ограничения видимости переменных:
'use strict';
{
// вспомогательные переменные, локальные для блока
let a = 5;
// поработали с ними
alert(a); // 5
// больше эти переменные не нужны
}
alert(a); // ошибка нет такой переменной
Конечно, это бывает удобно, но в данном случае это создаст проблему при деструктуризации:
let a, b;
{a, b} = {a:5, b:6}; // будет ошибка, оно посчитает, что {a,b} - блок
Чтобы избежать интерпретации {a, b}
как блока, нужно обернуть всё присваивание в скобки:
let a, b;
({a, b} = {a:5, b:6}); // внутри выражения это уже не блок
Вложенные деструктуризации
Если объект или массив содержат другие объекты или массивы, и их тоже хочется разбить на переменные – не проблема.
Деструктуризации можно как угодно сочетать и вкладывать друг в друга.
В коде ниже options
содержит подобъект и подмассив. В деструктуризации ниже сохраняется та же структура:
'use strict';
let options = {
size: {
width: 100,
height: 200
},
items: ["Пончик", "Пирожное"]
}
let { title="Меню", size: {width, height}, items: [item1, item2] } = options;
// Меню 100 200 Пончик Пирожное
alert(title); // Меню
alert(width); // 100
alert(height); // 200
alert(item1); // Пончик
alert(item2); // Пирожное
Как видно, весь объект options
корректно разбит на переменные.
Итого
-
Деструктуризация позволяет разбивать объект или массив на переменные при присвоении.
-
Синтаксис:
let {prop : varName = default, ...} = object
Здесь двоеточие
:
задаёт отображение свойстваprop
в переменнуюvarName
, а равенство=default
задаёт выражение, которое будет использовано, если значение отсутствует (не указано илиundefined
).Для массивов имеет значение порядок, поэтому нельзя использовать
:
, но значение по умолчанию – можно:let [var1 = default, var2, ...rest] = array
Объявление переменной в начале конструкции не обязательно. Можно использовать и существующие переменные. Однако при деструктуризации объекта может потребоваться обернуть выражение в скобки.
-
Вложенные объекты и массивы тоже работают, при деструктуризации нужно лишь сохранить ту же структуру, что и исходный объект/массив.
Как мы увидим далее, деструктуризации особенно удобны при чтении объектных параметров функций.