Концепции деструктурирования, параметров 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) 

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

Output1 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) 

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

Output1 

Также вы можете деструктурировать значения вложенных объектов. Например, обновите объект 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}`) 

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

OutputSherlock 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) 

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

Output8 

Строка 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) 

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

Output1970 12 01 

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

// Skip the second item in the array const [year, , day] = date  console.log(year) console.log(day) 

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

Output1970 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) 

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

Output1 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}`) } 

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

Outputid: 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) выведет на экран примерно следующее:

OutputFri 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) 

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

Outputid: 3 name: "Ron" organization: {position: "Director"} 

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

user.organization.position = 'Director' 

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

const updatedUser = {   ...user,   organization: {     ...user.organization,     position: 'Director',   }, }  console.log(updatedUser) 

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

Outputid: 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) 

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

Output6 

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

const numbers = [1, 2, 3]  multiply(...numbers) 

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

Output6 

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

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

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

Output6 

Теперь вы увидели, как можно использовать 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) 

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

Output1 2 [3, 4, 5, 6] 

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

function testArguments() {   console.log(arguments) }  testArguments('how', 'many', 'arguments') 

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

Output1Arguments(3) ["how", "many", "arguments"] 

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

const testArguments = () => {   console.log(arguments) }  testArguments('how', 'many', 'arguments') 

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

OutputUncaught 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) 

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

Outputhammer ["screwdriver", "wrench"] 

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

const { isLoggedIn, ...rest } = { id: 1, name: 'Ben', isLoggedIn: true }  console.log(isLoggedIn) console.log(rest) 

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

Outputtrue {id: 1, name: "Ben"} 

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

Заключение

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

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

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

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