One place for hosting & domains

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

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

      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.



      Source link