Автор выбрал 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')
Результат выглядел бы так:
Output
1Arguments(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.