Спред-синтаксис ...

Упрощает создание объектов и массивов на основе других объектов и массивов

Время чтения: 5 мин

Кратко

Секция статьи "Кратко"

Спред-синтаксис (spread) ... позволяет передавать итерируемые коллекции (например, массивы или строки) как список аргументов функции или добавлять содержащиеся в них элементы в новый массив.

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

Пример

Секция статьи "Пример"

При вызове функции использовать значения из массива как аргументы:

        
          
          function multiplyThreeNumbers(a, b, c) {  return a * b * c}const nums = [1, 2, 3]console.log(multiplyThreeNumbers(...nums))// 6
          function multiplyThreeNumbers(a, b, c) {
  return a * b * c
}

const nums = [1, 2, 3]

console.log(multiplyThreeNumbers(...nums))
// 6

        
        
          
        
      

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

        
          
          const donor = ['это', 'старые', 'значения']const newArray = [...donor, 1, true, 'мама']console.log(newArray)// ['это', 'старые', 'значения', 1, true, 'мама']
          const donor = ['это', 'старые', 'значения']
const newArray = [...donor, 1, true, 'мама']

console.log(newArray)
// ['это', 'старые', 'значения', 1, true, 'мама']

        
        
          
        
      

У объекта скопировать свойства из другого объекта в новый:

        
          
          const persona = { name: 'Иван', lastName: 'Объектов'}const userData = { ...persona, username: 'killer3000' }console.log(userData)// {//    name: "Иван",//    lastName: "Объектов",//    username: "killer3000"// }
          const persona = { name: 'Иван', lastName: 'Объектов'}
const userData = { ...persona, username: 'killer3000' }

console.log(userData)
// {
//    name: "Иван",
//    lastName: "Объектов",
//    username: "killer3000"
// }

        
        
          
        
      

Как понять

Секция статьи "Как понять"

Спред-синтаксис легче всего изучать на примерах. Есть три контекста, в которых он применяется.

При вызове функции

Секция статьи "При вызове функции"

Часто встречается ситуация, когда мы хотим использовать данные из итерируемой коллекции в качестве аргументов функции. Чаще всего такая коллекция — массив. Если функция не умеет принимать массив аргументом, то придётся доставать элементы вручную:

        
          
          function multiplyThreeNumbers(a, b, c) {  return a * b * c}const nums = [1, 2, 3]console.log(multiplyThreeNumbers(nums[0], nums[1], nums[2]))// 6
          function multiplyThreeNumbers(a, b, c) {
  return a * b * c
}

const nums = [1, 2, 3]

console.log(multiplyThreeNumbers(nums[0], nums[1], nums[2]))
// 6

        
        
          
        
      

Если элементов становится больше, доставать значения вручную становится неудобно. Чтобы решить эту проблему, в старых версиях языка использовали метод apply. Этот метод принимает первым аргументом значение this, а вторым — список аргументов для вызова функции:

        
          
          function multiplyThreeNumbers(a, b, c) {  return a * b * c}const nums = [1, 2, 3]console.log(multiplyThreeNumbers.apply(null, nums))// 6
          function multiplyThreeNumbers(a, b, c) {
  return a * b * c
}

const nums = [1, 2, 3]

console.log(multiplyThreeNumbers.apply(null, nums))
// 6

        
        
          
        
      

Такой синтаксис сложно читается, его нельзя использовать при создании объектов с помощью конструктора new. Его упростили до спред-синтаксиса. В этом случае элементы как бы выкладываются из списка в нужном порядке:

        
          
          function multiplyThreeNumbers(a, b, c) {  return a * b * c}const nums = [1, 2, 3]console.log(multiplyThreeNumbers(...nums))// 6
          function multiplyThreeNumbers(a, b, c) {
  return a * b * c
}

const nums = [1, 2, 3]

console.log(multiplyThreeNumbers(...nums))
// 6

        
        
          
        
      

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

        
          
          function multiplyThreeNumbers(a, b, c) {  return a * b * c}const nums = [1, 2, 3, 5, 6]console.log(multiplyThreeNumbers(...nums))// 6
          function multiplyThreeNumbers(a, b, c) {
  return a * b * c
}

const nums = [1, 2, 3, 5, 6]

console.log(multiplyThreeNumbers(...nums))
// 6

        
        
          
        
      

При создании массивов с помощью литерала []

Секция статьи "При создании массивов с помощью литерала []"

Спред-синтаксис решает задачу создания нового массива с использованием данных из другого массива. Без него неудобно создавать массив, который содержит элементы другого. Приходится использовать методы массива, например, concat:

        
          
          const watchedMovies = ['Rocky', 'Terminator 2', 'The Matrix']const watchedVideos = ['Rick&Morty', 'lofi hip hop radio'].concat(watchedMovies)console.log(watchedVideos)// ['Rick&Morty', 'lofi hip hop radio', 'Rocky', 'Terminator 2', 'The Matrix']
          const watchedMovies = ['Rocky', 'Terminator 2', 'The Matrix']
const watchedVideos = ['Rick&Morty', 'lofi hip hop radio'].concat(watchedMovies)

console.log(watchedVideos)
// ['Rick&Morty', 'lofi hip hop radio', 'Rocky', 'Terminator 2', 'The Matrix']

        
        
          
        
      

Спред решает эту проблему лучше:

        
          
          const watchedMovies = ['Rocky', 'Terminator 2', 'The Matrix']const watchedVideos = ['Rick&Morty', 'lofi hip hop radio', ...watchedMovies]console.log(watchedVideos)// ['Rick&Morty', 'lofi hip hop radio', 'Rocky', 'Terminator 2', 'The Matrix']
          const watchedMovies = ['Rocky', 'Terminator 2', 'The Matrix']
const watchedVideos = ['Rick&Morty', 'lofi hip hop radio', ...watchedMovies]

console.log(watchedVideos)
// ['Rick&Morty', 'lofi hip hop radio', 'Rocky', 'Terminator 2', 'The Matrix']


        
        
          
        
      

Таким образом можно создать копию существующего массива:

        
          
          const watchedMovies = ['Rocky', 'Terminator 2', 'The Matrix']const myWatchedMovies = [...watchedMovies]
          const watchedMovies = ['Rocky', 'Terminator 2', 'The Matrix']
const myWatchedMovies = [...watchedMovies]

        
        
          
        
      

Или склеить несколько массивов в один:

        
          
          const movies = ['Rocky', 'Terminator 2', 'The Matrix']const series = ['Prison Break', 'Rick&Morty', 'Lost']const watched = [...movies, ...series]// [//  "Rocky",//  "Terminator 2",//  "The Matrix",//  "Prison Break",//  "Rick&Morty",//  "Lost"// ]
          const movies = ['Rocky', 'Terminator 2', 'The Matrix']
const series = ['Prison Break', 'Rick&Morty', 'Lost']

const watched = [...movies, ...series]
// [
//  "Rocky",
//  "Terminator 2",
//  "The Matrix",
//  "Prison Break",
//  "Rick&Morty",
//  "Lost"
// ]

        
        
          
        
      

Пример поведения с уровнем вложенности больше одного:

        
          
          const users = [{ name: 'Иван', lastName: 'Объектов' }]const copyUsers = [...users]copyUsers[0].name = 'Николай'console.log(users[0])// { name: 'Николай', lastName: 'Объектов' }
          const users = [{ name: 'Иван', lastName: 'Объектов' }]
const copyUsers = [...users]

copyUsers[0].name = 'Николай'
console.log(users[0])
// { name: 'Николай', lastName: 'Объектов' }

        
        
          
        
      

При создании объекта с помощью литерала {}

Секция статьи "При создании объекта с помощью литерала {}"

По аналогии с массивами, спред-синтаксис решает проблему копирования свойств в новый объект. В версии языка без спреда для копирования использовался метод Object.assign, который принимал два объекта — куда копировать свойства и откуда:

        
          
          const person = { name: 'Иван', lastName: 'Объектов' }const userData = Object.assign({ username: 'killer3000' }, person)console.log(userData)// {//    name: "Иван",//    lastName: "Объектов",//    username: "killer3000"// }
          const person = { name: 'Иван', lastName: 'Объектов' }
const userData = Object.assign({ username: 'killer3000' }, person)

console.log(userData)
// {
//    name: "Иван",
//    lastName: "Объектов",
//    username: "killer3000"
// }

        
        
          
        
      

Спред упрощает код и делает его читабельнее:

        
          
          const person = { name: 'Иван', lastName: 'Объектов' }const userData = { username: 'killer3000', ...person }console.log(userData)// {//    name: "Иван",//    lastName: "Объектов",//    username: "killer3000"// }
          const person = { name: 'Иван', lastName: 'Объектов' }
const userData = { username: 'killer3000', ...person }

console.log(userData)
// {
//    name: "Иван",
//    lastName: "Объектов",
//    username: "killer3000"
// }

        
        
          
        
      

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

        
          
          const person = { name: 'Иван', lastName: 'Объектов' }const userData = { name: 'Николай', ...person }console.log(userData)// { name: "Иван", lastName: "Объектов" }
          const person = { name: 'Иван', lastName: 'Объектов' }
const userData = { name: 'Николай', ...person }

console.log(userData)
// { name: "Иван", lastName: "Объектов" }

        
        
          
        
      

Если поставить спред в начало, то будет использоваться новое имя:

        
          
          const person = { name: 'Иван', lastName: 'Объектов' }const userData = { ...person, name: 'Николай' }console.log(userData)// { name: "Николай", lastName: "Объектов" }
          const person = { name: 'Иван', lastName: 'Объектов' }
const userData = { ...person, name: 'Николай' }

console.log(userData)
// { name: "Николай", lastName: "Объектов" }

        
        
          
        
      

На практике

Секция статьи "На практике"

nlopin

Секция статьи "nlopin"

🛠 Спред на объектах используют в библиотеке React. Программист может передать в функцию объект с произвольными свойствами, но мы хотим запретить устанавливать некоторые из них. В этом случае создаётся новый объект, в который сначала копируются присланные значения, а потом значения «запрещённых» свойств. В этом случае даже если запрещённое свойство было передано, оно будет перезаписано:

        
          
          function Headline(props) {  const filteredProps = {...props, ariaHidden: false}  return createElement('h1', filteredProps, 'Текст заголовка')}
          function Headline(props) {
  const filteredProps = {...props, ariaHidden: false}
  return createElement('h1', filteredProps, 'Текст заголовка')
}