Это не первая статья на Medium об операторе спреда и не последняя. Тем не менее, если вы запутались в этих загадочных эллипсах в Javascript и среди всех других наткнулись на мою статью, добро пожаловать! Я проведу вас через это.

Что такое оператор спреда?

Оператор распространения выглядит как ... и, в двух словах, преобразует массив (или объект) только в его элементы.

let arr = [1, 2, 3];
console.log(...arr); // 1 2 3

Этот синтаксис является новым в ES6, поэтому вы, возможно, не сталкивались с ним, если изучали Javascript по устаревшим материалам.

Это хорошо, но как мне это использовать?

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

Копировать массив

Самый простой (но не самый простой) способ скопировать массив - использовать цикл for:

let arr = [1, 2, 3];
let copyOfArr = [];
for (let i = 0; i < arr.length; i++) {
   copyOfArr.push(arr[i]);
}
console.log(copyOfArr); // [1, 2, 3]

Более смекалистый подход использует Array.slice():

let arr = [1, 2, 3];
let copyOfArr = arr.slice(0);
console.log(copyOfArr); // [1, 2, 3]

Но проще всего использовать оператор спреда:

let arr = [1, 2, 3];
let copyOfArr = [...arr];
console.log(copyOfArr); // [1, 2, 3]

Оператор распространения берет отдельные элементы из arr и распределяет (помещает) их в наш новый массив. Обратите внимание, что это отличается от записи [arr]:

// NOT WHAT WE WANT
let arr = [1, 2, 3];
let copyOfArr = [arr];
console.log(copyOfArr); // [[1, 2, 3]]

«Но почему мы не можем просто написать let copyOfArr = arr

Хороший вопрос. Ответ: в некоторых ситуациях можно. ОДНАКО массивы и объекты Javascript передаются по ссылке, а не по значению. Это означает, что когда мы пишем let copyOfArr = arr, наша новая переменная copyOfArr на самом деле не является копией arr - это ссылка, которая указывает на arr. Итак, если мы изменим arr, то изменится и copyOfArr.

let arr = [1, 2, 3];
let copyOfArr = arr;
arr.pop();
console.log(copyOfArr); // [1, 2]

Я предполагаю, что если бы мы хотели, чтобы copyOfArr изменялся каждый раз, когда arr изменения, мы, вероятно, вообще не сделали бы копию.

Добавить элемент в конец массива

Теперь, когда мы понимаем копирование, следующие примеры будут проще. Предположим, нам нужен новый массив со всем содержимым arr, за исключением того, что теперь с новым элементом в конце. Похоже, это работа для Array.push().

let arr = [1, 2, 3];
let newArray = [...arr];
newArray.push(4);
console.log(newArray); // [1, 2, 3, 4]

Однако, как оказалось, оператор спреда настолько мощный, что нам даже не нужно Array.push()! Мы будем использовать оператор распространения, чтобы создать новый массив со всеми значениями arr, за которым следует наш новый элемент или элементы:

let arr = [1, 2, 3];
let newArray = [...arr, 4];
console.log(newArray); // [1, 2, 3, 4]

Добавить элемент в начало массива

Вы, наверное, видите, к чему все идет:

let arr = [1, 2, 3];
let newArray = [0, ...arr];
console.log(newArray); // [0, 1, 2, 3]

Поместите значения массива в середину нового массива

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

let arr = [1, 2, 3];
let newArray = [0, ...arr, 4, 5];
console.log(newArray); // [0, 1, 2, 3, 4, 5]

Объединить (объединить) массивы

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

let oneTwo = [1, 2];
let threeFour = [3, 4];
let newArray = [...oneTwo, ...threeFour];
console.log(newArray); // [1, 2, 3, 4]

Получите максимальное значение массива

Я видел, как программисты сортируют массивы от самого высокого до самого низкого, а затем возвращают первый элемент отсортированного массива.

let values = [4, 1, 2, 5, 0];
let highestValue = values.sort((a, b) => b - a)[0];
console.log(highestValue); // 5

Если вы хотите нарядиться, вот решение, в котором используется Array.reduce():

let values = [4, 1, 2, 5, 0];
let highestValue = values.reduce((acc, val) => (val > acc ? val : acc), 0);
console.log(highestValue); // 5

Однако наиболее логичным решением было бы использовать Math.max(). Загвоздка в том, что мы должны передавать в Math.max() отдельные аргументы, а это значит, что мы не можем использовать Math.max() в массиве.

let values = [4, 1, 2, 5, 0];
let highestValue = Math.max(values);
console.log(highestValue); // NaN

Если бы только существовал способ взять массив и разложить его отдельные элементы… о, подождите!

let values = [4, 1, 2, 5, 0];
let highestValue = Math.max(...values);
console.log(highestValue); // 5

Теперь, когда мы знаем об операторе распространения, эти другие методы массива кажутся обходными по сравнению.

Получите наименьшее значение массива

Достаточно просто:

let values = [4, 1, 2, 5, 0];
let lowestValue = Math.min(...values);
console.log(lowestValue); // 0

Удалить индекс из массива

Мы могли бы использовать для этого Array.splice(), но давайте представим, что мы не хотим изменять исходный массив.

let arr = [1, 2, 7, 3, 4, 5, 6, 7];
// The erroneous 7 is at index 2:
let i = 2;
let newArray = [...arr.slice(0, i), ...arr.slice(i + 1)];
console.log(newArray); // [1, 2, 3, 4, 5, 6, 7]

Обновить объект

У вас может возникнуть ситуация, когда вам нужно вернуть объект с обновленными данными, но без изменения исходного объекта. Обратите внимание, разработчики React: это часто будет иметь место, если вы используете Redux! К счастью, оператор распространения работает с объектами. Если мы распространяем два объекта на новый объект, и оба объекта имеют общий ключ, последний объект заменит первый своим значением для этого ключа. Результат - новый объект с обновленными данными.

let youtubeVideo = {
    title: "PB&J tutorial (GONE WRONG) (POLICE CALLED)",
    likes: 2,
    dislikes: 1000,
    description: "Smash that like button and hit SUBSCRIBE!",
    url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
};
let updateData = {
    dislikes: youtubeVideo.dislikes + 1
}
let updatedVideo = {
    ...youtubeVideo, ...updateData
};
console.log(updatedVideo); // same as youtubeVideo, but now with 1001 dislikes

Остаточный синтаксис

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

function getAverage(...numbers) {
    return numbers.reduce((acc, val) => acc + val) / numbers.length;
}
console.log(getAverage(5, 10, 50, 25, 35)); // 25

Синтаксис отдыха с React

Давайте дополним эту статью отличным примером. Допустим, у нас есть некоторые пользовательские данные, которые мы хотим преобразовать в список. Каждый раз, когда мы визуализируем список в React, нам нужно присвоить каждому элементу списка уникальный атрибут key. Мы используем id каждого пользователя в качестве key и пишем что-то вроде этого:

Это достаточно просто, но что, если бы у нас были еще более подробные данные о пользователях? Представим, что мы также хотели отобразить родной город наших пользователей, адрес электронной почты, любимую еду, рост, вес, худшие опасения и т. Д. Внезапно мы обнаруживаем, что деструктурируем и выписываем множество реквизитов для UserListItem в нашей функции map().

К счастью, у нас есть более простой способ. Мы деструктурируем id для каждого пользователя, а затем воспользуемся синтаксисом rest для доступа к остальным свойствам пользователя. Затем мы перенесем эти свойства в наш UserListItem компонент.

Наш код по-прежнему работает так же, и теперь у нас есть более гибкая и лаконичная map() функция.

Если мы действительно хотим сделать все возможное, мы можем использовать синтаксис rest в определении компонента UserListItem:

Надеюсь, это поможет, и спасибо за чтение!

Подписывайтесь на меня в LinkedIn и GitHub