Классическая проблема
Бывали ли вы когда-нибудь в ситуации, когда вам нужно было создать метод, принимающий неопределенное количество аргументов? Вы бы сказали, что это несложно, поскольку Javascript предоставляет нам всемогущие 'аргументы' как локальные , подобные массиву (обратите внимание - подобный массиву не является массивом ) для использования в каждой функции. Итак, допустим, если мы хотим создать метод для вычисления суммы чисел, не ограничиваясь тем, сколько чисел ему передано, мы можем реализовать как:
function sumOf(){ var sum = 0; for (var i = 0; i < arguments.length; i++){ sum += arguments[i]; } return sum; }
Легкая штука, правда? И, конечно же, он отлично работает.
sumOf(1,2,3,4,5);//15 sumOf(1,2);//3
Прохладный. Но ... что, если нам нужен метод, который после этого получает один параметр и неограниченное количество аргументов, потому что мы можем решить только на основе первого аргумента, что делать со всеми другими переданными аргументами (только с ними , не считая первого)? Например:
function execute(action, <param1, param2, param3,...>){ if (action === <something>){ doSomething(<param1, param2, param3, ...>) } }
Как этого добиться? Вы можете
Попробуйте применить ()
Функция apply () хороша тем, что apply () принимает в качестве аргументов единственный массив параметров (или объект, подобный массиву), что означает Мы сможем:
Обратной стороной является слишком много работы:
- Необходимо удалить первый аргумент из списка аргументов.
- Поскольку аргументы только подобны массиву, отсутствует встроенный shift () или splice (), мы должны сначала преобразовать его в массив, а только затем удалить первый аргумент.
Остальное просто - вызовите действие с помощью apply()
и передайте null как значение this
и измененный массив.
Вопрос: есть ли более короткий и лучший способ достичь того же результата?
Благодаря ES6 у нас наконец-то появилась магия (…) синтаксиса распространения.
Что такое синтаксис magic spread?
Проще говоря, расширенный синтаксис - записанный как «…» - позволяет:
- Итерируемый объект
- Объект выражение
будет расширен в местах, где ожидается:
- Нуль
- Неограниченное количество аргументов (вызовы функций)
- Неограниченное количество элементов (литералы массива)
- Неограниченное количество пар ключ-значение (объектные литералы)
Как это решает нашу проблему?
Прежде всего, мы можем использовать синтаксис распространения в качестве параметра Rest.
Остаточный параметр
Параметр rest позволяет функции получать неопределенное количество аргументов в виде массива.
функция ‹имя функции› (… аргументы)
Примечание: синтаксис распространения (…) необходимо использовать как префикс для последнего указанного параметра.
В отличие от arguments
, параметр Rest:
- на самом деле является экземплярами Array с полной поддержкой встроенных функций Array.
- содержит только те, которым не присвоено отдельное имя в качестве элементов массива, а
arguments
содержит все переданные параметры.
function printMe(a,b,c){ console.log(arguments); //will print all passed parameters respectively as a, b, c } function printMeSpread(a, ...args){ console.log(args); //Will only print from 2nd passed parameters onwards } printMe(1,2,3);// [1,2,3] printMeSpread(1,2,3); //[2,3]
Следовательно, вместо преобразования arguments
в массив, удаления первого элемента (который нам не нужен), мы можем просто переписать объявление нашей функции как:
function executeWithSpread(type, ...params){....}
И благодаря этому мы сэкономили около 2–3 строк кода. Что делать дальше?
Избавляемся от apply()
Действительно, мы можем использовать его как замену apply()
, тогда наша функция станет:
Здесь, когда мы используем синтаксис «…» в качестве префикса params
, все элементы params
будут распространяться (расширяться) как аргументы, а не быть одним аргументом params
.
Дополнительные пояснения:
- На
executeWithSpread()
параметры будут преобразованы в массив, содержащий [1,2,3,4,5], с использованием...params
в объявлении метода. - На
Sum()
, передав параметры как...params
,arguments
будет[1,2,3,4,5]
вместо[[1,2,3,4,5]]
И, следовательно, нет необходимости в null
или в каком-либо дополнительном ненужном шуме.
Но если вы все еще хотите комбинировать с apply()
, мы все равно можем сделать следующее:
В любом случае, это еще чище, меньше шансов на ошибку, согласны?
Но не только это, как уже говорилось, синтаксис распространения творит чудеса. Под «магией» я подразумеваю мощный инструмент, несмотря на его внешнюю простоту. Итак…
Что еще он нам предлагает?
Клонировать объект
Один из распространенных способов копирования объекта - использовать Object.assign()
для создания неглубокой версии клона.
Синтаксис распространения дает нам более короткий и понятный способ
Подобно Object.assign()
, использование синтаксиса spread (…) будет только: я инициализирую копирование собственных перечислимых свойств объекта в новый объект.
Клонировать массив
Вместо использования поверхностного копирования массива с использованием Array.slice(0)
Или используя Array.from()
Мы можем использовать синтаксис Spread для достижения того же эффекта - но помните, что это будет только один уровень глубины, как Array.slice()
и Array.from()
Преобразование итерируемого объекта в массив
Вероятно, многие из нас уже знали и использовали эту функцию 😃 - помните печально известную проблему с удалением дубликатов из заданного массива? 😁
Да, вместо использования цикла для итерации и копирования каждого элемента в новый массив мы можем сократить код, используя синтаксис Spread (…), как указано выше. Он распределит все элементы Set в новый массив соответственно без особых усилий (конечно, меньше шансов для 🐛).
То же самое можно сказать и о преобразовании объекта Map в массив, в котором каждая пара ‹ключ, значение› будет представлена как элемент подмассива в новом массиве.
❌ Но определенно невозможно преобразовать Object, поскольку он не повторяется.
Объединить массивы
Обычно, если мы хотим объединить два или более массивов в один или просто хотим объединить один массив с другим, мы будем использовать
Array.prototype.concat()
, который вернет новый массив, в котором все элементы дополнительного массива будут добавлены в конец исходного массива. Это не изменит исходный массив.
Array.prototype.unshift()
, который добавит все элементы дополнительного массива в начало исходного массива. Это действительно изменит исходный массив.
Однако с помощью синтаксиса распространения мы можем легко определить порядок слияния следующим образом:
Обратите внимание, что в этом случае оба исходных массива не изменяются.
Свойства объектов слияния
Буквально, какой из них вы предпочитаете? Таким образом (который изменяет исходный объект)
for(var key in obj2){ obj1[key] = obj2[key]; }
Или просто так
Object.assign(obj1, obj2)
Или еще проще? (не изменяет исходный объект)
var mereObj = {...obj1, ...obj2}
Использование с конструктором
При использовании конструктора с синтаксисомnew
невозможно напрямую использовать apply()
и массив вместе - в ES5, чтобы создать новый массив из существующего массива, нам нужно будет сделать следующее:
var arr = new (Function.prototype.bind.apply(Array, [null].concat([1,2,3,4])));
Разве это не сбивает с толку? По крайней мере, для меня это так. К счастью, для нас есть синтаксис Spread:
var arr = new Array(...[1,2,3,4]);
Разве мы не должны ценить эту магию 😃?
Другое смешанное использование
И последнее, но не менее важное: с помощью синтаксиса Spread мы, наконец, можем получить гораздо более мощный литерал массива, в котором нам не нужно запрашивать помощь дополнительных функций, таких как push
, splice
, concat
и т. Д., Когда это необходимо.
var arr1 = ['hello', 'there']; var arr2 = ['today', 'is', 'good', 'day', ...arr1, 'how', 'are', 'you'] console.log(arr2); //['today', 'is', 'good', 'day', 'hello', 'there', 'how', 'are', 'you']
Отлично, правда?
И, конечно же, нет ограничений на количество раз, которое мы можем использовать этот синтаксис в литерале массива.
Заключение
Лично я обнаружил, что этот синтаксис скорее полезен, чем следование традиционному способу - , который требует длинной строки кода, и если вы не будете осторожны, вы можете получить 🐛 , не заметив, и что, меня это очень раздражает.
Однако в обмен на чистоту и удобство иногда бывает труднее читать / понимать. (в конце концов, впервые-r (s), кто сможет понять, что означает связка «…»?)
Несмотря на это, как гласит одно из правил №1 в программировании - правило KISS (Keep It Simple Stupid), мы всегда должны стараться, чтобы наш код был как можно проще, и меньше кода но хороший код никогда не бывает плохим (особенно в JS, когда количество строк кода должно быть загружено ДЕЙСТВИТЕЛЬНО имеет значение для веб-производительности).
Так почему не? Если это хорошая магия, нам лучше воспользоваться ею, как лучшим улучшением для нашего собственного волшебного творения. 😄
Вы согласны со мной? Напишите в комментариях. Я хотел бы услышать ваше мнение.
Подробнее о ES6:
- Set vs Array - что и когда?
- Карта против объекта - что и когда?
- ES6 Крутые штуки - var, let и const в глубину
- ES Cool stuffs - Подробное заявление о деструктуризации
- Крутые штуки для ES6 - подробности о шаблонных литералах
- Давайте разделим наши телефоны на классы
Если вам понравился этот пост, не забудьте поставить мне 👏 ниже ⏬️. Это наверняка меня сильно мотивирует 😊
Если вам нравится читать больше, ознакомьтесь с моими статьями.
Если вы хотите иногда меня догнать, подпишитесь на меня в Twitter | Facebook или просто посетите мой сайт портфолио.