5 интересных приёмов с деструктуризацией в JavaScript

Перевод статьи 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.

Photo by Sigmund on Unsplash

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]

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх