Оператор распространения в JavaScript

Введение В этом руководстве мы исследуем одну из мощных функций спецификации JavaScript ES6 - оператор распространения. Хотя синтаксис прост, иногда реализация сбивает с толку, если вы не понимаете ее должным образом. В этом уроке мы демистифицируем эти три точки ... JavaScript, который делает удивительные вещи с итерациями. Использование оператора распространения Есть разные способы использования оператора распространения и каждой цели использования для решения разных постановок задачи.

Вступление

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

Использование оператора спреда

Оператор распространения и каждая цель использования используются по-разному для решения разных постановок проблемы.

Расширяющиеся массивы

Мы можем использовать оператор распространения для итераций, таких как String или массив, и он поместит содержимое итерации в отдельные элементы.

Например:

 let greet = ['Hello', 'World']; 
 console.log(greet); // Without spread operator 
 console.log(...greet); // Using spread operator 

Если мы запустим этот код, мы увидим следующее:

 ['Hello', 'World'] 
 Hello World 

Вы, должно быть, заметили, что во втором случае (с оператором распространения) содержимое greet было расширено и выброшено из массива.

Иногда нам может потребоваться преобразовать String в список символов. Мы можем использовать оператор распространения для этого варианта использования:

 let greetings = "hello"; 
 let chars = [...greetings]; 
 console.log(chars); 

Если мы запустим этот код, нас встретят:

 [ 'h', 'e', 'l', 'l', 'o' ] 

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

Объединение массивов

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

 let blog1Subscribers = [' [email protected] ', ' [email protected] ']; 
 let blog2Subscribers = [' [email protected] ', ' [email protected] ', ' [email protected] ']; 
 let subscribers = [...blog1Subscribers, ...blog2Subscribers]; 
 console.log(subscribers); 

Если мы запустим приведенный выше код, мы получим единый список итераций. Это стало возможным, поскольку и ...blog1Subscribers и ...blog2Subscribers были рассредоточены, а [] действовал как "получатель", который эффективно объединял элементы распространения в единый список элементов.

Примечание. Оператору распространения нужно, чтобы получатель поместил развернутое значение. Если вы опустите приемник, он выдаст ошибку.

Мы также можем использовать оператор распространения внутри Array.push() чтобы протолкнуть содержимое одного массива в другой:

 let arr1 = ['John', 'Sofia', 'Bob']; 
 let arr2 = ['Julia', 'Sean', 'Anthony']; 
 arr1.push(...arr2); 
 console.log(arr1); 

Если мы запустим этот код, мы увидим следующий вывод:

 [ 'John', 'Sofia', 'Bob', 'Julia', 'Sean', 'Anthony' ] 

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

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

 let arr1 = ['John', 'Sofia', 'Bob']; 
 let arr2 = arr1; 
 console.log(arr2); 
 arr1.push('Sally'); // Change arr1 
 console.log(arr2); 

 [ 'John', 'Sofia', 'Bob' ] 
 [ 'John', 'Sofia', 'Bob', 'Sally' ] 

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

 let arr1 = ['John', 'Sofia', 'Bob']; 
 let arr2 = [...arr1]; 
 console.log(arr2); 
 arr1.push('Sally'); // Change arr1 
 console.log(arr2); 

Выполнение этого кода приводит к следующему:

 [ 'John', 'Sofia', 'Bob' ] 
 [ 'John', 'Sofia', 'Bob' ] 

Как мы видим, в arr2 не была передана ссылка, как раньше, а вместо этого он был заполнен значениями arr1 как полностью новый объект. Таким образом, даже когда arr1 изменяется, arr2 остается неизменным.

Мы также можем использовать оператор распространения для создания копии массива и одновременного добавления в него новых элементов:

 let arr1 = ['John', 'Sofia', 'Bob']; 
 let arr2 = [...arr1, 'Anthony', 'Sean']; 
 console.log(arr2); 

 ['John', 'Sofia', 'Bob', 'Anthony', 'Sean'] 

Примечание. Оператор распространения работает со всеми итерациями, включая объекты.

Раньше для добавления новых элементов в новый массив требовалась дополнительная строка кода.

Точно так же мы можем копировать объекты, используя оператор распространения:

 let o1 = { a: 1, b: 2 }; 
 let o2 = { c: 3, d: 4, ...o1 }; 
 console.log(o2); 

 { c: 3, d: 4, a: 1, b: 2 } 

Как видим, мы успешно скопировали объект o1 в o2 .

Эта функция имеет множество реальных вариантов использования. Например, предположим, что мы сохранили информацию о регистрации пользователя в объекте. Мы можем сделать мелкую копию этого объекта и добавить дополнительную информацию в скопированный объект:

 let user = { name: 'John', email: ' [email protected] ' }; 
 let _user = { ...user, ...getSession(user) }; 
 console.log(_user); 

 { name: 'John', email: ' [email protected] ', 'token': 'abc123', 'expiresAt': 1565630480671 } 

Нам также может потребоваться объединить платежную информацию и информацию о доставке в одну:

 const billing = { billingContact: '0987654321', billingAddress: 'street no 123, xyz city' }; 
 const shipping = { shippingContact: '123456789', shippingAddress: 'street no 999, abc city' }; 
 const custInfo = { ...billing, ...shipping }; 
 console.log(custInfo); 

Если мы запустим этот код, нас должны встретить:

 { 
 billingContact: '0987654321', 
 billingAddress: 'street no 123, xyz city', 
 shippingContact: '123456789', 
 shippingAddress: 'street no 999, abc city' 
 } 

Здесь можно задать один вопрос. Что, если оба объекта имеют одни и те же свойства.

В случае противоречия свойств преимущество последнего объекта. Давайте посмотрим на это на примере:

 const o1 = { a: 1, b: 2 }; 
 const o2 = { b: 3, c: 4, ...o1}; 
 console.log(o2); 

Если вы запустите этот код, вы должны увидеть следующее:

 { b: 2, c: 4, a: 1 } 

Как видим по свойствам второй объект o2 выигрывает. Однако, если сначала поставить оператор спреда:

 const o1 = { a: 1, b: 2 }; 
 const o2 = { ...o1, b: 3, c: 4}; 
 console.log(o2); 

 { a: 1, b: 3, c: 4 } 

Мы видим, что свойство из o1 выигрывает, что имеет смысл, поскольку o2 - последний объект.

Одним из вариантов использования этой функции может быть выполнение назначений по умолчанию:

 const userProvided = { 
 name: 'Bil Smith', 
 email: ' [email protected] ', 
 }; 
 const defaultValues = { 
 name: 'Unknown', 
 address: 'Alien', 
 phone: null, 
 email: null 
 }; 
 const userInfo = { ...defaultValues, ...userProvided }; 

Альтернатива вызову функций с помощью apply ()

Допустим, функция принимает аргумент - список оценок пяти лучших учеников в классе. У нас также есть список из внешнего источника. Конечно, мы можем избежать передачи отдельных элементов и вместо этого передать весь список, используя метод apply() :

 myFun(m1, m2, m3, m4, m5) { 
 // Do something 
 } 
 
 let marks = [10, 23, 83, -1, 92]; 
 myFun.apply(undefined, arr); 

Мы можем избавиться от запутанного undefined аргумента и сделать код более чистым, вызвав функцию напрямую с помощью оператора распространения:

 myFun(m1, m2, m3, m4, m5) { 
 // Do something 
 } 
 
 let marks = [10, 23, 83, -1, 92]; 
 myFun(...marks); 

Использование с математическими функциями

В JavaScript есть Math который содержит несколько методов для работы с набором данных, то есть списком данных.

Допустим, мы хотим получить максимальное значение из первых трех чисел списка:

 let mylist = [10, 23, 83, -1, 92, -33, 76, 29, 76, 100, 644, -633]; 
 Math.max(mylist[0], mylist[1], mylist[2]); 

Что, если мы хотим получить максимум всех чисел в списке? Что, если в списке n элементов? Конечно, нам не нужны mylist[0], mylist[1]... mylist[1000] .

Оператор распространения предлагает более чистое решение:

 let mylist = [10, 23, 83, -1, 92, -33, 76, 29, 76, 100, 644, -633]; 
 Math.max(...mylist); 

Примечание. Учитывая, что оператор распространения работает как с массивами, так и с объектами, иногда может возникнуть соблазн смешать и сопоставить их. Не делай этого! Например, следующее действие приведет к ошибке:

 let user = {name:'John', age:28, email:' [email protected] '}; 
 let items = [...user]; 

 TypeError: user is not iterable 

Оператор спреда против параметра отдыха

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

Как мы узнали, оператор распространения расширяет содержимое итерации. Напротив, оператор rest собирает все оставшиеся элементы в массив.

 function doSum(...items) { 
 let sum = 0; 
 for (let item of items){ 
 sum += item; 
 } 
 return sum; 
 } 
 
 doSum(1); 
 doSum(1,2); 
 doSum(1, 2, 3, 4); 

Если мы запустим приведенный выше код, нас встретит следующее:

 1 
 3 
 6 
 10 

Как мы видим, каждый раз оставшиеся элементы собирались параметром Rest .

Мы также можем предоставить отдельные переменные для некоторых элементов и сделать так, чтобы параметр rest собирал остальные элементы. Единственное условие - оставшийся параметр всегда должен быть последним параметром функции:

 function doSum(times, ...items) { 
 let sum = 0; 
 for (let item of items){ 
 sum += item*times; 
 } 
 return sum; 
 } 
 
 doSum(1, 1); 
 doSum(2, 1, 2); 
 doSum(3, 1, 2, 3); 

Если мы запустим приведенный выше код, мы увидим следующее:

 1 
 6 
 18 

Заключение

Как мы видим, оператор распространения ... - действительно мощная функция спецификации JavaScript ES6. С помощью этого оператора мы можем легко решить множество реальных проблем. Как мы узнали из различных примеров, обсуждаемых в этой статье, это позволяет нам писать меньше кода и делать больше.

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

comments powered by Disqus