Разбор структуры в JavaScript для массивов и объектов
Изучите деструктуризацию в JavaScript с простыми английскими объяснениями: пропуск элементов массива и значение по умолчанию, псевдонимы объектов, вложенная деструктуризация, замена значений и параметры функций (ES6+).
Разбор структуры означает взятие структурированного значения и его разделение так, чтобы каждая часть могла использоваться отдельно. Вместо многократного обращения к одному и тому же контейнеру вы указываете ожидаемую структуру и присваиваете имена элементам за один шаг.
Простая аналогия — это извлечение нескольких личных вещей из путевого рюкзака. Рюкзак символизирует массив, объект, строку или любую структуру, которая ведёт себя как итерируемый объект. Личные вещи — это отдельные значения, которые вы хотите использовать. Итераторы и итерируемые объекты заслуживают отдельного подробного рассмотрения позже; пока достаточно знать, что массивы и многие встроенные функции уже поддерживают такую возможность разбора структуры.
С помощью этой модели мышления элементы структуры можно более чисто присвоить переменным, чем писать отдельные выражения с индексами для каждого элемента:
const numbers = [ 1, 2, 3 ];
// En lugar de realizar esto:
let x = numbers[0];
let y = numbers[1];
let z = numbers[2];
// Simplemente podemos hacer esto:
let [ x, y, z ] = numbers;
console.log(x); // 1
console.log(y); // 2
console.log(z); // 3
Во время выполнения одного и того же задания вы можете пропускать значения, которые вас не интересуют, оставляя пустые промежутки между запятыми. Это позволяет сохранить необходимые связи без необходимости создания дополнительных переменных для их хранения:
const numbers = [ 1, 2, 3 ];
// x = 1, z = 3
let [ x, , z ] = numbers;
// y = 2, z = 3
let [ , y, z ] = numbers;
// x = 1, y = 2, z = 3, a = undefined
let [ x, y, z, a ] = numbers;
Вы также можете задавать новые значения по умолчанию, когда какое-либо поле отсутствует или не определено, что удобно для необязательных позиций в конце массива:
const numbers = [ 1, 2, 3 ];
// x = 1, y = 2, z = 3, a = 4
let [ x, y, z, a = 4 ] = numbers;
Вложенные структуры распаковываются аналогичным образом — можно обращаться к элементам внутреннего массива без использования временных хранилищ, существующих лишь для обработки одного уровня структуры:
const alphanumericSequence = [ 'a', 'b', 'c', [1, 2, 3] ];
// numericSequence = [ 1, 2, 3 ]
let [ , , , numericSequence ] = alphanumericSequence;
// x = 1, y = 2, z = 3
let [ , , , [ x, y, z ] ] = alphanumericSequence;
Практический трюк — это обмен двумя переменными без создания временной переменной. Операция деструктуризации позволяет обменять значениями с обеих сторон в одном операторе, что устраняет распространенные ошибки типа «ошибка на единицу» при ручном использовании третьей переменной:
let a = 1;
let b = 2;
// En lugar de hacer esto:
let temp = a;
a = b;
b = temp;
// Simplemente podemos hacer esto:
[ a, b ] = [ b, a ];
console.log(a); // 2
console.log(b); // 1
До сих пор «рюкзак» представлял собой массив, в котором порядок определяет значение. Объекты распаковываются по немного иной схеме: за связывание отвечают имена свойств, а не порядок.
Распаковка объектов
Когда структурой является объект, имена переменных-получателей должны совпадать с именами свойств, извлекаемых из объекта — если только вы не переименуете их с помощью псевдонимов. Именно это правило является основным отличием от распаковки массива, где важен только порядок.
Короткий пример иллюстрирует правило совпадения имён для простой записи сотрудника:
const employee = {
name: 'Julia',
age: 27,
job: {},
workSchedule: {}
};
// El nombre de las variables coincide con el nombre
// de las propiedades del objeto.
let { name, age, job, workSchedule } = employee;
Каждое имя переменной совпадает со свойством объекта, поэтому локальные идентификаторы остаются самодокументируемыми.
Если имена этих свойств неудобны в локальном области видимости или перекрываются с уже существующими идентификаторами, присвойте псевдонимы, сохраняя при этом оригинальные ключи:
let {
name:employeeName,
age:employeeAge,
job:employeeJob,
workSchedule:employeeWorkSchedule
} = employee;
Значения по умолчанию также можно применять во время разбора структуры, заполняя пробелы при отсутствии или неопределенности свойства без дополнительной проверки на значение null позже:
let {
name = 'john',
age = 18,
job = 'worker',
workSchedule = null
} = employee;
// Incluso utilizar un alias durante este proceso:
let {
name:employeeName = 'john',
age:employeeAge = 18,
job:employeeJob = 'worker',
workSchedule:employeeWorkSchedule = null
} = employee;
Вложенные объекты разбираются по тому же принципу: указывается путь в шаблоне, и привязываются только те элементы, которые вам нужны:
const employee = {
name: 'Julia',
age: 27,
job: {},
workSchedule: {
mon: {
open: 12,
close: 22,
},
tue: {
open: 11,
close: 23,
},
wed: {
open: 23,
close: 5,
}
}
};
let { workSchedule: { mon, tue, wed } } = employee;
console.log(mon); // { open: 12, close: 22 }
console.log(tue); // { open: 11, close: 23 }
console.log(wed); // { open: 23, close: 5 }
При глубокой вложенности следует проявлять осторожность. Получение значений через множество уровней в одном шаблоне быстро снижает читаемость и делает рефакторинг хрупким при изменении структуры:
const employee = {
name: 'Julia',
age: 27,
workSchedule: {
mon: {
open: {
from: 12
},
close: {
to: 22
}
}
}
};
// Multiples niveles de anidación
let { workSchedule: { mon: { open: {from}, close: {to} } } } = employee;
console.log(from); // 12
console.log(to); // 22
Разбор объектов также полезен для параметров функций. Вместо длинного списка позиционных аргументов можно использовать объект с опциями, разбирая его поля в нужных местах с применением значений по умолчанию:
const dispatchDelivery = (endpoint, { userId, time = '00:00', address, items = [] }) => {
// Some action...
};
dispatchDelivery('api/dispatch-delivery', {
userId: 3392,
address: 'Villas del Sol',
time: '22:30',
items: [ 'Beers', 'Sodas', 'Doritos' ]
});
dispatchDelivery('api/dispatch-delivery', {
userId: 5423,
address: 'Villas del Sol'
});
В результате получается более насыщенный и удобочитаемый сигнатур функции — особенно когда ей требуется множество параметров, которые вызывающие код не всегда передают. Разбор структур является основным инструментом современного JavaScript, и он становится ещё более мощным в сочетании с оператором spread, который позволяет использовать дополнительные способы комбинирования структур для их копирования и объединения.
Обратите внимание на базовые требования языка: разбор структур появился в ECMAScript 2015 (ES6). Старые движки требуют этапа транспайлинга или использования стратегии полифилов, прежде чем его можно будет применять в производственных браузерах или устаревших средах выполнения.