Перевод статьи 5 Interesting uses of JavaScript destructuring! от Yuma-Tsushima.


При написании кода на JavaScript я регулярно сталкиваюсь с деструктурирующим присваиванием.
Чтение свойств объектов и обращение к элементам массива – довольно распространённые операции. Деструктуризация значительно упрощает подобные действия.
В этой статье я опишу 5 интересных и нетривиальных способов использования деструктуризации в JavaScript.
1. Подмена переменных
Обычно для того, чтобы поменять местами 2 переменные, требуется ещё одна временная переменная. Рассмотрим простой сценарий:
let a = 1; let b = 2; let temp; temp = a; a = b; b = temp; a; // => 2 b; // => 1
temp
– временная переменная, которая хранит значение a
. Далее a
присваивается значение b
, а переменной b
, в свою очередь, присваивается temp
.
Деструктурирующее присваивание же делает всё проще, устраняя необходимость во временной переменной:
let a = 1; let b = 2; [a, b] = [b, a]; a; // => 2 b; // => 1
Здесь [a, b] = [b, a]
– это деструктурирующее присваивание. Справа создаётся массив [b, a]
со значениями [2, 1]
. Первый элемент в нём (равен 2) присваивается a
, второй элемент (равен 1) – помещается в b
.
Хоть вы и создаёте временный массив, код всё равно получается более лаконичным.
Но это ещё не всё. Таким образом можно сразу поменять и более 2 переменных. Попробуем так:
let zero = 2; let one = 1; let two = 0; [zero, one, two] = [two, one, zero]; zero; // => 0 one; // => 1 two; // => 2
Вы можете менять столько переменных, сколько захотите! Но случай с двумя наиболее распространён.
2. Обращение к элементу массива
У вас есть массив, который потенциально может быть пуст. Вы хотите обратиться к первому, второму, n-ному элементу, но, если такового не существует, хотите получать значение по умолчанию.
Обычно мы пользуемся свойством length
массива:
const colors = []; let firstColor = 'white'; if (colors.length > 0) { firstColor = colors[0]; } firstColor; // => 'white'
К счастью, деструктуризация массива поможет вам решить задачу за куда меньшее число действий:
const colors = []; const [firstColor = 'white'] = colors; firstColor; // => 'white'
В строчке const [firstColor = 'white'] = colors
в переменную firstColor
помещается первый элемент массива colors
. Если в массиве не будет элемента с индексом 0, будет присвоено значение по умолчанию – 'white'
.
3. Принцип неизменности
Когда я начал использовать React, а позже – Redux, я был вынужден писать код, который отвечал принципу неизменности (immutability). Сперва было нелегко, но позже я осознал преимущества такого подхода: стало проще работать с однонаправленным потоком данных.
Принцип неизменности запрещает менять объекты. К счастью, деструктуризация позволяет производить некоторые операции, не нарушая данное правило.
Деструктуризация в сочетании с оператором ...
удаляет элементы из начала массива:
const numbers = [1, 2, 3]; const [, ...fooNumbers] = numbers; fooNumbers; // => [2, 3] numbers; // => [1, 2, 3]
Деструктуризация в строчке [, ...fooNumbers] = numbers
создаёт новый массив fooNumbers
, который содержит все элементы из numbers
, кроме первого.
Массив numbers
не меняется, а значит, принцип неизменности соблюдается.
Точно так же вы можете удалять свойства объектов. Попробуем удалить свойство foo
из объекта big
:
const big = { foo: 'value Foo', bar: 'value Bar' }; const { foo, ...small } = big; small; // => { bar: 'value Bar' } big; // => { foo: 'value Foo', bar: 'value Bar' }
Деструктурирующее присваивание в сочетании с ...
создаёт новый объект small
со всеми свойствами big
, кроме foo
.
4. Деструктуризация итерируемых объектов
В предыдущих примерах мы использовали деструктуризацию с массивами. Однако, этот приём применим к любым объектам, которые реализуют протокол «Итерируемый» (iterable protocol).
Множество встроенных типов и объектов являются итерируемыми: массивы, строки, типизированные массивы, set и map.
const str = 'cheese'; const [firstChar = ''] = str; firstChar; // => 'c'
Но мы не ограничены лишь встроенными типами. Логику деструктуризации можно настроить и самому, реализовав данный протокол.
movies
содержит список объектов-фильмов. При деструктуризации movies
было бы неплохо получить каждый фильм в формате строки. Реализуем собственный метод для итерации:
const movies = { list: [ { title: 'Skyfall' }, { title: 'Interstellar' } ], [Symbol.iterator]() { let index = 0; return { next: () => { if (index < this.list.length) { const value = this.list[index++].title; return { value, done: false }; } return { done: true }; } }; } }; const [firstMovieTitle] = movies; console.log(firstMovieTitle); // => 'Skyfall'
Объект movies
реализует итерируемый протокол при определении метода Symbol
.
iterator
, который итерируется по названиям фильмов.
Так как объект movies
соответствует протоколу, мы можем его деструктурировать. В данном случае мы прочитали название первого фильма: const [firstMovieTitle] = movies
.
Существует ещё много способов использовать деструктуризацию вместе с итерируемыми объектами.
5. Деструктуризация динамических свойств
По моему опыту, деструктурировать объекты приходится чаще, чем массивы.
В случае объекта это выглядит довольно просто:
const movie = { title: 'Skyfall' }; const { title } = movie; title; // => 'Skyfall'
При присваивании const { title } = movie
создаётся переменная title
, и её значение помещается в свойство movie.title
.
В первый раз читая про деструктуризацию объектов, я был немного удивлён, что для этого не требуется знать название свойства. Объект можно деструктурировать с помощью динамических свойств!
Чтобы понять, как работает динамическая деструктуризация, напишем функцию greet()
, которая будет выводить приветствие:
function greet(obj, nameProp) { const { [nameProp]: name = 'Unknown' } = obj; return `Hello, ${name}!`; } greet({ name: 'Ben' }, 'name'); // => 'Hello, Ben!' greet({ }, 'name'); // => 'Hello, Unknown!'
greet
()
вызывается с 2 аргументами: объект и имя свойства.
Внутри greet()
деструктурирующее присваивание const { [nameProp]: name = 'Unknown' } = obj
получает динамическое имя свойства при помощи квадратных скобок [nameProp]
. Переменной name
присваивается значение динамического свойства.
Более того, вы можете указать значение '
Unknown
'
по умолчанию на случай, если нужного свойства не существует.
Заключение
Деструктуризация будет отличным помощником при обращении к свойствам объектов или элементам массивов.
Помимо базового использования, этот приём полезен для замены переменных, обращения к элементам массива и следования принципу неизменности.
Помимо всего прочего, в JavaScript можно реализовывать собственную логику деструктуризации.
[customscript]techrocks_custom_after_post_html[/customscript]
[customscript]techrocks_custom_script[/customscript]