В ES6 появились операторы Spread (расширение) и Rest (остаток), которые позволяют простым образом объединять несколько массивов в один или передавать значения отдельных элементов массива в качестве аргументов функций. Интересно, что оба эти оператора внешне выглядят абсолютно одинаково - это троеточие (...), но используются в разных случаях, причем не только для массивов, но и для объектов.

Рассмотрим их применение на нескольких типичных задачах.

Оператор spread

Оператор разворота spread используется для:

  1. объединения 2-х или более массивов в один
  2. копирования данных из одного массива в другой
  3. передачи массива в качестве аргументов функции
  4. применения методов объекта Math
  5. массива в качестве данных объекта Date
  6. разбиения строк на символы
  7. литералов объекта

Оператор spread ... в переводе с английского - это распространение, расширение или разворот позволит нам склеить 2 массива в один. Например, нам зачем-то захотелось объединить зиму с летом. Попробуем сделать это методами массива push(), concat() и оператором spread:

Примечание: строчки с winter.push(summer) и console.log(winter) нужно закомментировать, прежде, чем применять метод concat() и оператор spread, т.к. он "вкладывает" сам второй массив внутрь первого, а не его значения.

В консоли вы увидите такие варианты объединения массивов:

Использование методов push(), concat() и оператора spread

Метод concat() объединил массивы, и то же самое сделал spread оперратор. Но что делать, если нам нужно поместить данные одного массива между данными другого массива? Т.е. нужно сделать примерно так:

В консоли вы увидите, что обычным способом мы поместили массив внутрь другого массива, а spread-оператор добавил значения массива в текущий массив.
Массив в массиве

Копирование данных из одного массива в другой

Еще один вариант применения оператора spread - это копирование данных из одного массива в другой. Если написать так:

В консоли получим такой результат:

Копирование массива

То есть, использовав код numbers2 = numbers мы на самом деле получили ссылку на первый массив numbers. И второй массив после добавления данных в первый изменился так же, как и первый. Если же задача заключается не в использовании 2-х переменных, ссылающихся на один массив, а в сохранении в одной из переменных старых данных из массива, например, для последующего сравнения, то как раз имеет смысл использовать оператор расширения. Несколько перепишем код выше:

В консоли видим, что одинаковые массивы после добавления данных перестали быть одинаковыми, т.е. копирование с помощью оператора spread создает нам новый массив.

Копирование массива с помощью spread-оператора

Можно также проверить, совпадают ли массивы

Примечание: до выхода стандарта ES6 копирование можно было сделать с помощью метода array.slice():

Попробуйте сами.

Spread в качестве аргументов функции

Еще одно применение оператора spread позволяет развернуть массив для использования значений его элементов в качестве аргументов функции. В идеале количество элементов массива должно совпадать с количеством  аргументов функции. Если элементов массива будет больше, то избыток проигнорируется, если меньше, то вместо отсутствующих элементов будет передано значение undefined. Например, нам нужно перемножить 3 числа:

Если элементов массива недостаточно, их можно заменить значениями по умолчанию. Перепишем нашу функцию таким образом:

Теперь функция будет выводить корректный результат умножения чисел, если их передается меньше 3-х или одно из значений в массиве undefined.

Для использования методов объекта Math

Например, существует задача найти минимальное и максимальное значение среди элементов массива. Можно решить ее, перебрав все элементы массива методом array.forEach() и сравнив их с неким начальным значением, например, с 0 или со значением 0-го элемента массива. То же самое можно сделать с помощью метода array.reduce():

Для нашего массива numbers это должны быть числа 99, как максимальное, и -12, как минимальное. Давайте посмотрим, что выведет скрипт:

Все работает верно.

Теперь попробуем еще один способ. Это использование метода apply() для передачи массива аргументов в метод Math.max()  или Math.min():

Проверяем на практике - видим те же значения:

И последнее решение, в котором мы будем использовать оператор spread:

Результат тот же, но кода совсем мало, и он легко читается.

Еще один плюс использования оператора spread в нашем примере заключается в том, что мы можем очень простым способом найти максимальное и минимальное значения не только в одном массиве, но и в 2-х, 3-х и более, не прибегая к их конкатенации (объединению). И даже можем добавить в скобки любое количество дополнительных чисел, которые не входят ни в один из массивов. Для этого нужно записать такой код:

Думаю, что вы быстро определите, какие числа попадут в переменные max3, max4 и min3, min4. Проверьте, верен ли код:

Для объекта Date

И это еще не все. Вы можете выполнить разворачивание массива в качестве данных объекта Date с помощью оператора spread и получить корректную дату:

Использование оператора spread для разбиения строк на символы

Не очень очевидное использование оператора расширения для строк позволит сделать из любой строки массив символов, чтобы выделить в строке каждую букву. Например, так:

Давайте посмотрим на результат:

Задача: попробуйте видоизменить код таким образом, чтобы каждая из букв была ссылкой на какую либо страницу сайта flibusta.is, например, так: E | F

А теперь несколько более впечатляющий пример с использованием для каждой буквы строки обертки в виде <span> с классом animCharacter, относительного позиционирования для него и анимации @keyframes:

See the Pen JS Spread operator for string by Elen (@ambassador) on CodePen.

Использование оператора spread для литерала объекта

С выходом стандарта ECMAScript 2018 стало возможным использование оператора spread для литералов объекта, например, для их копирования:

В переменной userLink мы получаем ссылку на основной объект user, поэтому после добавления в user свойства id, меняются оба объекта, ведь фактически они указывают путь к одному и тому же литералу объекта. А в переменной userCopy мы сохранили данные объекта user в его первоначальном виде.

Копирование объекта оператором spread

О других способах копирования объектов читайте в специальной статье, посвященной этому.

Оператор расширения также может нам помочь в случае, если нужно объединить ряд данных, принадлежащих по сути одному объекту, но сохраненных в разных. Например, у нас есть 3 объекта, хранящие тип, возраст и цену одного и того же кота. Мы можем их объединить в один объект, но разница между использованием обычного объекта и полученного за счет оператора разворота очевидна:

В консоли видно, что оператор расширения собрал все данные в один объект из разных, а не добавил 3 объекта с похожими данными:

Оператор spread и литерал объекта

Ряд интересных примеров с манипуляциями свойствами литералов объектов вы найдете в статье Rest и Spread в JavaScript. Возможности, о которых вы не знали, а также в справке на MDN.

Преобразование коллекции DOM элементов

При выборе различными методами коллекций html-элементов в DOM-модели браузера мы получаем псевдомассивы, к которым далеко не всегда можно применить методы встроенного объекта Array, с помощью которых очень удобно выполнять какие-либо одинаковые операции.

Например, метод document.querySelectorAll() реализован таким образом, что возвращает коллекцию NodeList, для которой можно вызвать метод forEach(), но не другие методы массивов. А свойство document.links или метод document.getElementsByClassName() вернет коллекцию HTMLCollection, для которой даже forEach() вызвать нельзя. Чтобы воспользоваться все-таки методами массивов, применим к ним оператор расширения и посмотрим, стали ли они массивами:

Все сработало и для NodeList, и для HTMLCollection.

В примере ниже при клике на кнопках вызываются функции, которые позволяют использовать методы или циклы для работ с массивами, чтобы управлять абзацами или ссылками

See the Pen Array from HTML Coollection with spread operator by Elen (@ambassador) on CodePen.

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

Оператор rest

Оператор rest (оставшиеся параметры) используется для указания массива в качестве аргументов функции. Например:

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

В том случае, если вам нужно, чтобы какие-то параметры задавались и использовались в функции обязательно, можно добавить оператор rest после нужных аргументов функции.

Допустим, нам нужно сделать простой калькулятор, который позволит указать операцию (сложение, вычитание, умножение и деление), число, с которым нужно произвести вычисление и еще ряд чисел, которые будут прибавляться, отниматься или выступать множителями или делителями. Поскольку оператор оставшихся параметров выдаст нам массив переданных в функцию значений, будет очень удобно использовать для вычислений метод array.reduce() с указанным начальным значением и стрелочными функциями в качестве callback-ов.

Опять-таки с помощью метода массива array.includes() мы сделали внутри функции проверку на деление на 0. Очень удобно использовать методы массивов, не так ли?

Следует понимать, что оператор rest (оставшиеся параметры) отличается от объекта arguments:

  • в объекте arguments содержатся все аргументы, передаваемые в функцию, а оставшиеся параметры включают только те, которым не задано отдельное имя;
  • объект arguments не является массивом, а оставшиеся параметры, наоборот, принадлежат классу Array и для них можно использовать любые методы массивов (forEach(), map(), reduce и др.), которые ранее были доступны только с помощью call() или apply();
  • объект arguments имеет дополнительные свойства (например, callee), характерные только для него .

Деструктивное присваивание

Синтаксис destructuring assignment - деструктивного, или деструктурирующего присваивания, предполагает, что в коде используется массив или литерал объекта.

Деструктивное присваивание для элементов массива

Оператор разворота spread позволял нам объединить 2 массива в один, а деструктурирующее присваивание - распределить данные между элементами массива и переменными. Например, у нас есть набор данных в виде массива и нам нужно присвоить значения его элементов, распределив их по определенным переменным. Это будет выглядеть так:

Обратите внимание, что в первом выводе в console.log() был сохранен порядок присвоения, а во втором - изменен. И тем не менее, значения этих переменных выведены именно в том виде, в котором произошло деструктивное присваивание.

Частичная деструктуризация

Деструктуризация может быть частичной, т.е. в процессе присваивания данных в переменные какие-либо из этих данных или переменных мы можем опустить. Например, из кода выше мы опустим часть данных в конце или в начале.

Оставшиеся параметры для деструкции

Деструкция может использовать оставшиеся параметры. Например, необходимо разбить строку на части, причем интересует нас первая буква, а остальные не слишком важны. Пример имеет такой код:

В результате мы получили букву "И" в качестве переменной first и массив остальных символов в переменной rest.

Давайте посмотрим, как мы можем использовать этот вариант на html-странице. например, у нас есть ряд комментариев, которые содержат имя автора комментария и сам комментарий. По задумке дизайнера первая буква каждого имени автора должна быть выделена красным цветом. Внешний вид примера таков:

Иван Довгий | 20.09.2019
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eligendi mollitia amet enim quis nulla explicabo!
Админ | 21.09.2019
Perferendis quas esse laboriosam harum quia aliquam! Dolores quidem, porro dolore temporibus unde consequatur nihil?
Сара Бернар | 28.09.2019
Alias eius ut, vero reprehenderit. Odio placeat, vel mollitia assumenda! Incidunt quos, nemo. Dicta, ad!
Novus | 01.10.2019
Cupiditate nesciunt ducimus culpa eveniet ex, sed possimus qui, animi deserunt eum! Similique omnis, doloremque.

Код окраски имени автора комментария базируется на предыдущем примере и выглядит так:

Здесь мы используем в качестве строки свойства textContent и innerHTML, т.к. именно они позволяют получить и переписать строку из JavaScript в HTML.

Деструктуризация для возврата значений из функций

Оператор return в функции позволяет вернуть какое-то одно значение. Оно может быть массивом или объектом, но в единсвенном экземпляре. А нам, предположим, нужно записать данные в 2 или более переменные. С деструктивным присваиванием это решается просто.

В примере нам нужно умножить 2 числа на разные множители и вернуть результат. Мы можем сделать это при однократном вызове функции.

 

Автор: Админ

Добавить комментарий

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