Главная / Статьи

Разбор структуры в JavaScript для массивов и объектов

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

Издатель: REACTAPP.TOP5 мин чтения1129 слов

Источники: Entendiendo la desestructuración en Javascript

Разбор структуры означает взятие структурированного значения и его разделение так, чтобы каждая часть могла использоваться отдельно. Вместо многократного обращения к одному и тому же контейнеру вы указываете ожидаемую структуру и присваиваете имена элементам за один шаг.

Простая аналогия — это извлечение нескольких личных вещей из путевого рюкзака. Рюкзак символизирует массив, объект, строку или любую структуру, которая ведёт себя как итерируемый объект. Личные вещи — это отдельные значения, которые вы хотите использовать. Итераторы и итерируемые объекты заслуживают отдельного подробного рассмотрения позже; пока достаточно знать, что массивы и многие встроенные функции уже поддерживают такую возможность разбора структуры.

С помощью этой модели мышления элементы структуры можно более чисто присвоить переменным, чем писать отдельные выражения с индексами для каждого элемента:

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). Старые движки требуют этапа транспайлинга или использования стратегии полифилов, прежде чем его можно будет применять в производственных браузерах или устаревших средах выполнения.