// Tutorial //

Концепции деструктурирования, параметров Rest и синтаксиса Spread в JavaScript

Published on June 11, 2020
Default avatar
By Tania Rascia
Developer and author at DigitalOcean.
Русский
Концепции деструктурирования, параметров Rest и синтаксиса Spread в JavaScript

Автор выбрал COVID-19 Relief Fund для получения пожертвования в рамках программы Write for DOnations.

Введение

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

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

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

Синтаксис деструктурирования позволяет задавать свойства объектов и элементы массива как переменные. Это значительно сокращает количество строк кода, необходимых для манипулирования данными в этих структурах. Существует два типа деструктурирования: деструктурирование объектов и деструктурирование массивов.

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

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

Рассмотрим пример объекта, представляющего собой заметку со свойствами id, title и date:

const note = {
  id: 1,
  title: 'My first note',
  date: '01/01/1970',
}

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

// Create variables from the Object properties
const id = note.id
const title = note.title
const date = note.date

С деструктурированием объектов можно уложиться в одну строку. При заключении каждой переменной в фигурные скобки {} JavaScript создаст новые переменные из каждого свойства с тем же именем:

// Destructure properties into variables
const { id, title, date } = note

Запустите console.log() для новых переменных:

console.log(id)
console.log(title)
console.log(date)

На экран будут выведены начальные значения свойств:

Output
1 My first note 01/01/1970

Примечание. Деструктурирование объекта не изменяет первоначальный объект. Вы все равно можете вызвать первоначальный объект note со всеми исходными записями.

При деструктурировании объектов создаются новые переменные с теми же именами, что и у свойств объекта. Если вы не хотите, чтобы имя новой переменной совпадало с именем свойства, вы можете переименовать новую переменную, используя двоеточие (:) для ввода нового имени, как показано в следующем примере с noteId:

// Assign a custom name to a destructured value
const { id: noteId, title, date } = note

Зарегистрируйте новую переменную noteId в консоли:

console.log(noteId)

Результат будет выглядеть следующим образом:

Output
1

Также вы можете деструктурировать значения вложенных объектов. Например, обновите объект note так, чтобы у него был вложенный объект author:

const note = {
  id: 1,
  title: 'My first note',
  date: '01/01/1970',
  author: {
    firstName: 'Sherlock',
    lastName: 'Holmes',
  },
}

Теперь вы можете деструктурировать объект note, а затем провести деструктурирование еще раз, чтобы создать переменные из свойств объекта author:

// Destructure nested properties
const {
  id,
  title,
  date,
  author: { firstName, lastName },
} = note

Затем зарегистрируйте новые переменные firstName и lastName, используя литерали шаблонов:

console.log(`${firstName} ${lastName}`)

Результат будет выглядеть следующим образом:

Output
Sherlock Holmes

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

// Access object and nested values
const {
  author,
  author: { firstName, lastName },
} = note

console.log(author)

Этот код выводит объект author:

Output
{firstName: "Sherlock", lastName: "Holmes"}

Деструктурирование объекта полезно не только для сокращения объема кода, но также позволяет организовать целевой доступ к важным свойствам.

Кроме того, деструктурирование можно использовать для доступа к свойствам объектов значений примитивов. Например, String — это глобальный объект для строк, и он имеет свойство length:

const { length } = 'A string'

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

console.log(length)

Результат будет выглядеть следующим образом:

Output
8

Строка A string была косвенно конвертирована в объект для получения свойства length.

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

Деструктурирование массивов позволяет создавтаь новые переменные, используя элементы массива в качестве значения. В качестве примера рассмотрим массив с разными компонентами даты:

const date = ['1970', '12', '01']

Массивы в JavaScript гарантированно сохраняют порядок, и поэтому первым индексом всегда будет год, вторым — месяц и т. д. Зная это, вы можете создавать переменные из элементов массива:

// Create variables from the Array items
const year = date[0]
const month = date[1]
const day = date[2]

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

// Destructure Array values into variables
const [year, month, day] = date

Зарегистрируйте новые переменные в журнале:

console.log(year)
console.log(month)
console.log(day)

Результат будет выглядеть следующим образом:

Output
1970 12 01

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

// Skip the second item in the array
const [year, , day] = date

console.log(year)
console.log(day)

При запуске этого кода будут указаны значения year и day:

Output
1970 01

Вложенные массивы также можно деструктурировать. Вначале создайте вложенный массив:

// Create a nested array
const nestedArray = [1, 2, [3, 4], 5]

Затем деструктурируйте массив и зарегистрируйте новые переменные:

// Destructure nested items
const [one, two, [three, four], five] = nestedArray

console.log(one, two, three, four, five)

Результат будет выглядеть следующим образом:

Output
1 2 3 4 5

Синтаксис деструктурирования можно применять для деструктурирования параметров функции. Для тестирования вам нужно будет деструктурировать ключи и значения из Object.entries().

Вначале декларируйте объект note:

const note = {
  id: 1,
  title: 'My first note',
  date: '01/01/1970',
}

Для этого объекта вы можете указать пары ключ-значение, деструктурируя аргументы по мере их передачи в метод forEach():

// Using forEach
Object.entries(note).forEach(([key, value]) => {
  console.log(`${key}: ${value}`)
})

Также вы можете использовать для этой цели цикл for:

// Using a for loop
for (let [key, value] of Object.entries(note)) {
  console.log(`${key}: ${value}`)
}

В каждом случае вы получите следующий результат:

Output
id: 1 title: My first note date: 01/01/1970

Деструктурирование объектов и деструктурирование массивов можно комбинировать в одном выражении деструктурирования. При деструктурировании также можно использовать параметры по умолчанию, как видно из этого примера, где задается дата по умолчанию new Date().

Вначале декларируйте объект note:

const note = {
  title: 'My first note',
  author: {
    firstName: 'Sherlock',
    lastName: 'Holmes',
  },
  tags: ['personal', 'writing', 'investigations'],
}

Затем деструктурируйте объект и задайте новую переменную new со значением по умолчанию new Date():

const {
  title,
  date = new Date(),
  author: { firstName },
  tags: [personalTag, writingTag],
} = note

console.log(date)

Команда console.log(date) выведет на экран примерно следующее:

Output
Fri May 08 2020 23:53:49 GMT-0500 (Central Daylight Time)

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

Spread

Синтаксис Spread (...) — это еще одно полезное дополнение JavaScript для работы с массивами, объектами и вызовами функций. Spread позволяет распаковывать или раскрывать объекты и элементы итерации (например, массивы) и использовать их для создания копий структур данных с целью упрощения манипуляций с данными.

Spread с массивами

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

// Create an Array
const tools = ['hammer', 'screwdriver']
const otherTools = ['wrench', 'saw']

Раньше нам нужно было бы использовать concat() для сокращения двух массивов:

// Concatenate tools and otherTools together
const allTools = tools.concat(otherTools)

Теперь мы также можем использовать spread для распаковки массивов в новый массив:

// Unpack the tools Array into the allTools Array
const allTools = [...tools, ...otherTools]

console.log(allTools)

Результат выполнения будет выглядеть так:

Output
["hammer", "screwdriver", "wrench", "saw"]

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

// Array of users
const users = [
  { id: 1, name: 'Ben' },
  { id: 2, name: 'Leslie' },
]

Вы можете использовать push для изменения массива и добавления нового пользователя, если это изменяемый объект:

// A new user to be added
const newUser = { id: 3, name: 'Ron' }

users.push(newUser)

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

Spread позволяет создать новый массив из существующего и добавить в его конец новый элемент:

const updatedUsers = [...users, newUser]

console.log(users)
console.log(updatedUsers)

Теперь в новый массив updatedUsers добавлен новый пользователь, а первоначальный массив users остался без изменений:

Output
[{id: 1, name: "Ben"} {id: 2, name: "Leslie"}] [{id: 1, name: "Ben"} {id: 2, name: "Leslie"} {id: 3, name: "Ron"}]

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

Рассмотрим этот пример, где мы создаем массив и назначаем его другой переменной:

// Create an Array
const originalArray = ['one', 'two', 'three']

// Assign Array to another variable
const secondArray = originalArray

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

// Remove the last item of the second Array
secondArray.pop()

console.log(originalArray)

Результат будет выглядеть следующим образом:

Output
["one", "two"]

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

Если вы напишете тот же код, но при этом скопируете массив с помощью spread, первоначальный массив больше не будет меняться:

// Create an Array
const originalArray = ['one', 'two', 'three']

// Use spread to make a shallow copy
const secondArray = [...originalArray]

// Remove the last item of the second Array
secondArray.pop()

console.log(originalArray)

На консоли будет зарегистрировано следующее:

Output
["one", "two", "three"]

Spread также можно использовать для конвертации набора или другого элемента с итерацией в массив.

Создайте новый набор и добавьте в него записи:

// Create a set
const set = new Set()

set.add('octopus')
set.add('starfish')
set.add('whale')

Используйте оператор spread с set и зарегистрируйте результаты:

// Convert Set to Array
const seaCreatures = [...set]

console.log(seaCreatures)

В результате вы получите следующий вывод:

Output
["octopus", "starfish", "whale"]

Это также может быть полезно при создании массива из строки:

const string = 'hello'

const stringArray = [...string]

console.log(stringArray)

Это даст нам массив, где каждый символ будет элементом массива:

Output
["h", "e", "l", "l", "o"]

Spread с объектами

При работе с объектами spread можно использовать для их копирования и обновления.

Изначально для копирования объектов использовался Object.assign():

// Create an Object and a copied Object with Object.assign()
const originalObject = { enabled: true, darkMode: false }
const secondObject = Object.assign({}, originalObject)

Теперь secondObject будет клоном originalObject.

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

// Create an object and a copied object with spread
const originalObject = { enabled: true, darkMode: false }
const secondObject = { ...originalObject }

console.log(secondObject)

Результат будет выглядеть следующим образом:

Output
{enabled: true, darkMode: false}

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

Spread упрощает добавление и изменение свойств существующего неизменяемого объекта. В этом примере мы добавляем свойство isLoggedIn в объект user:

const user = {
  id: 3,
  name: 'Ron',
}

const updatedUser = { ...user, isLoggedIn: true }

console.log(updatedUser)

В результате вы получите следующий вывод:

Output
{id: 3, name: "Ron", isLoggedIn: true}

При обновлении объектов с помощью spread важно учитывать, что каждый вложенный объект также потребуется передать. Рассмотрим пример, когда в объекте user содержится вложенный объект organization:

const user = {
  id: 3,
  name: 'Ron',
  organization: {
    name: 'Parks & Recreation',
    city: 'Pawnee',
  },
}

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

const updatedUser = { ...user, organization: { position: 'Director' } }

console.log(updatedUser)

Результат будет выглядеть следующим образом:

Output
id: 3 name: "Ron" organization: {position: "Director"}

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

user.organization.position = 'Director'

Однако нам нужно изменяемое решение, и мы используем spread для копирования внутреннего объекта для сохранения имеющихся свойств:

const updatedUser = {
  ...user,
  organization: {
    ...user.organization,
    position: 'Director',
  },
}

console.log(updatedUser)

В результате вы получите следующий вывод:

Output
id: 3 name: "Ron" organization: {name: "Parks & Recreation", city: "Pawnee", position: "Director"}

Spread с вызовами функций

Spread также можно использовать с аргументами в вызовах функций.

Например, у нас имеется функция multiply, которая берет три параметра и умножает их:

// Create a function to multiply three items
function multiply(a, b, c) {
  return a * b * c
}

Обычно мы передаем три переменных по отдельности как аргументы вызова функции, примерно так:

multiply(1, 2, 3)

Результат будет выглядеть следующим образом:

Output
6

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

const numbers = [1, 2, 3]

multiply(...numbers)

Это даст тот же результат:

Output
6

Примечание. Без spread этого можно добиться с помощью apply():

multiply.apply(null, [1, 2, 3])

Это даст нам следующее:

Output
6

Теперь вы увидели, как можно использовать spread для сокращения кода и можете рассмотреть другой вариант использовать параметров ... синтаксиса: rest.

Параметры Rest

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

Например, если в функции restTest мы захотим использовать массив args, состоящий из неограниченного количества аргументов, мы получим следующее:

function restTest(...args) {
  console.log(args)
}

restTest(1, 2, 3, 4, 5, 6)

Все аргументы, переданные в функцию restTest, теперь доступны в массиве args:

Output
[1, 2, 3, 4, 5, 6]

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

function restTest(one, two, ...args) {
  console.log(one)
  console.log(two)
  console.log(args)
}

restTest(1, 2, 3, 4, 5, 6)

При этом будут отдельно приниматься два аргумента, а остальные будут сгруппированы в массив:

Output
1 2 [3, 4, 5, 6]

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

function testArguments() {
  console.log(arguments)
}

testArguments('how', 'many', 'arguments')

Результат выглядел бы так:

[secondary_label Output]1
Arguments(3) ["how", "many", "arguments"]

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

const testArguments = () => {
  console.log(arguments)
}

testArguments('how', 'many', 'arguments')

При этом будет возникать ошибка:

Output
Uncaught ReferenceError: arguments is not defined

Кроме того, arguments не является истинным массивом и не может использовать такие методы как map и filter без предварительной конвертации в массив. Он будет собирать все передаваемые аргументы, а не только остальные аргументы, как показано в примере restTest(one, two, ...args).

Rest можно использовать и при деструктурировании массивов:

const [firstTool, ...rest] = ['hammer', 'screwdriver', 'wrench']

console.log(firstTool)
console.log(rest)

Это даст нам следующее:

Output
hammer ["screwdriver", "wrench"]

Также Rest можно использовать при деструктурировании объектов:

const { isLoggedIn, ...rest } = { id: 1, name: 'Ben', isLoggedIn: true }

console.log(isLoggedIn)
console.log(rest)

Результат будет выглядеть так:

Output
true {id: 1, name: "Ben"}

Таким образом, синтаксис rest дает эффективные методы для сбора неопределенного количества элементов.

Заключение

В этой статье мы рассказали о деструктурировании, синтаксисе spread и параметрах rest. Краткое содержание:

  • Деструктурирование используется для создания переменных из элементов массива или свойств объекта.
  • Синтаксис Spread используется для распаковки элементов с итерацией, таких как массивы, объекты и вызовы функций.
  • Синтаксис параметра Rest создает массив из неограниченного количества значений.

Деструктурирование, параметры rest и синтаксис spread — полезные функции JavaScript, позволяющие сохранять код лаконичным и чистым.

Если вы хотите увидеть деструктурирование в действии, пройдите обучающий модуль «Настройка компонентов React с помощью Props», где этот синтаксис используется для деструктурирования данных и их передачи компонентам клиентской части. Если вы хотите узнать больше о JavaScript, вернитесь на страницу серии статей по программированию на JavaScript.


Want to learn more? Join the DigitalOcean Community!

Join our DigitalOcean community of over a million developers for free! Get help and share knowledge in our Questions & Answers section, find tutorials and tools that will help you grow as a developer and scale your project or business, and subscribe to topics of interest.

Sign up
About the authors
Default avatar
Developer and author at DigitalOcean.

Default avatar
Senior Technical Editor

Editor at DigitalOcean, fiction writer and podcaster elsewhere, always searching for the next good nautical pun!


Still looking for an answer?

Was this helpful?
Leave a comment

This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!