← JavaScript/Деструктурирующее присваивание#70 из 383← ПредыдущийСледующий →+25 XP
Полезно по теме:Гайд: как учить JavaScriptПрактика: JS базаПрактика: async и сетьТермин: Closure

Деструктурирующее присваивание

Реальная проблема: работа с API-ответами

Когда вы делаете запрос к Stripe API, приходит большой объект: { id, amount, currency, customer: { id, email, name }, metadata: {...} }. Без деструктуризации нужно писать response.customer.email, response.customer.name десятки раз. Деструктуризация позволяет извлечь нужные поля в переменные за одну строку.

Что решает деструктуризация

  • Устраняет повторяющийся доступ obj.field.subfield
  • Делает параметры функций самодокументирующими
  • Упрощает работу с массивами (пропуск элементов, первый/остаток)
  • На основе предыдущих уроков

  • «Объекты» — синтаксис объектов, доступ к свойствам
  • «Массивы» — работа с массивами, индексы
  • «Rest/Spread» — ...rest в деструктуризации (забегаем вперёд: тот же синтаксис)
  • Деструктуризация объектов

    const user = { name: 'Алексей', age: 28, role: 'admin', city: 'Москва' }
    
    // Базовое — извлекаем нужные поля
    const { name, age } = user
    console.log(name, age)  // 'Алексей' 28
    
    // Переименование — новое имя переменной после :
    const { name: userName, role: userRole } = user
    console.log(userName, userRole)  // 'Алексей' 'admin'
    
    // Значение по умолчанию — если поля нет или оно undefined
    const { city, country = 'Россия' } = user
    console.log(city, country)  // 'Москва' 'Россия'
    
    // Вложенные объекты
    const response = { data: { user: { email: 'alex@mail.ru' } } }
    const { data: { user: { email } } } = response
    console.log(email)  // 'alex@mail.ru'
    
    // Остаток через ...rest
    const { name: n, ...rest } = user
    console.log(rest)  // { age: 28, role: 'admin', city: 'Москва' }

    Деструктуризация массивов

    const colors = ['red', 'green', 'blue', 'yellow']
    
    // Базовое — позиции важны, не имена
    const [first, second] = colors
    console.log(first, second)  // 'red' 'green'
    
    // Пропуск элементов — запятые без переменной
    const [, , third] = colors
    console.log(third)  // 'blue'
    
    // Значение по умолчанию
    const [a, b, c, d, e = 'white'] = colors
    console.log(e)  // 'white' — пятого элемента нет
    
    // Остаток
    const [head, ...tail] = colors
    console.log(head)  // 'red'
    console.log(tail)  // ['green', 'blue', 'yellow']
    
    // Обмен переменных без temp
    let x = 1, y = 2
    ;[x, y] = [y, x]
    console.log(x, y)  // 2 1

    В параметрах функции

    Очень часто в реальном коде — самодокументирующие параметры с дефолтами:

    // Вместо function createUser(name, role, theme, fontSize)
    function createUser({ name, role = 'user', theme = 'dark', fontSize = 14 } = {}) {
      console.log(`Создан: ${name} [${role}]`)
    }
    
    createUser({ name: 'Иван' })
    // Создан: Иван [user]
    
    createUser({ name: 'Мария', role: 'admin', theme: 'light' })
    // Создан: Мария [admin]

    Типичные ошибки

    Ошибка 1: деструктуризация null/undefined

    // Сломано:
    const { name } = null   // TypeError: Cannot destructure property 'name' of null
    
    // Исправлено — значение по умолчанию для всего объекта:
    const { name } = null ?? {}         // name = undefined
    const { name = 'Гость' } = null ?? {}  // name = 'Гость'

    Ошибка 2: путаница переименования и значения по умолчанию

    // Это переименование: новая переменная называется 'userName'
    const { name: userName } = user
    
    // Это значение по умолчанию:
    const { name = 'Гость' } = user
    
    // Оба вместе:
    const { name: userName = 'Гость' } = user

    Ошибка 3: деструктуризация в цикле — повторное объявление

    // Сломано:
    for (const item of items) {
      const { name } = item  // OK — const создаётся заново в каждой итерации
    }
    
    // Но вне цикла нельзя объявить дважды:
    const { name } = item1
    const { name } = item2  // SyntaxError: Identifier 'name' has already been declared
    
    // Исправлено — переименование:
    const { name: name1 } = item1
    const { name: name2 } = item2

    В реальных проектах

  • React: const { data, loading, error } = useQuery()
  • Node.js: const { PORT = 3000, DB_URL } = process.env
  • API-ответы: const { results: products, total, page } = await fetchProducts()
  • Импорты: import { useState, useEffect } from 'react' — тоже деструктуризация
  • Примеры

    Обработка ответа платёжного API (Stripe-подобный)

    // Симуляция ответа платёжного API
    const paymentResponse = {
      id: 'pay_3N8abc',
      status: 'succeeded',
      amount: 149900,  // в копейках
      currency: 'rub',
      customer: {
        id: 'cus_X7zZ',
        email: 'buyer@example.com',
        name: 'Алексей Смирнов',
        address: {
          city: 'Москва',
          street: 'ул. Тверская, 1',
        }
      },
      metadata: {
        orderId: 'ORD-2024-001',
        promoCode: 'WINTER20',
      },
      fees: [150, 75, 25]
    }
    
    // Деструктуризация вложенного объекта
    const {
      id: paymentId,
      status,
      amount,
      currency,
      customer: {
        email,
        name: customerName,
        address: { city }
      },
      metadata: { orderId, promoCode = null },
      fees: [mainFee, ...otherFees],
    } = paymentResponse
    
    console.log(paymentId)    // 'pay_3N8abc'
    console.log(status)       // 'succeeded'
    console.log(customerName) // 'Алексей Смирнов'
    console.log(city)         // 'Москва'
    console.log(orderId)      // 'ORD-2024-001'
    console.log(promoCode)    // 'WINTER20'
    console.log(mainFee)      // 150
    console.log(otherFees)    // [75, 25]
    console.log((amount / 100).toFixed(2) + ' ' + currency)  // '1499.00 rub'
    
    // Деструктуризация в параметрах функции
    function sendReceipt({ email, name: customerName, address: { city } = {} }) {
      console.log(`Квитанция отправлена ${customerName} (${email}) из ${city}`)
    }
    
    sendReceipt(paymentResponse.customer)
    // Квитанция отправлена Алексей Смирнов (buyer@example.com) из Москва

    Деструктурирующее присваивание

    Реальная проблема: работа с API-ответами

    Когда вы делаете запрос к Stripe API, приходит большой объект: { id, amount, currency, customer: { id, email, name }, metadata: {...} }. Без деструктуризации нужно писать response.customer.email, response.customer.name десятки раз. Деструктуризация позволяет извлечь нужные поля в переменные за одну строку.

    Что решает деструктуризация

  • Устраняет повторяющийся доступ obj.field.subfield
  • Делает параметры функций самодокументирующими
  • Упрощает работу с массивами (пропуск элементов, первый/остаток)
  • На основе предыдущих уроков

  • «Объекты» — синтаксис объектов, доступ к свойствам
  • «Массивы» — работа с массивами, индексы
  • «Rest/Spread» — ...rest в деструктуризации (забегаем вперёд: тот же синтаксис)
  • Деструктуризация объектов

    const user = { name: 'Алексей', age: 28, role: 'admin', city: 'Москва' }
    
    // Базовое — извлекаем нужные поля
    const { name, age } = user
    console.log(name, age)  // 'Алексей' 28
    
    // Переименование — новое имя переменной после :
    const { name: userName, role: userRole } = user
    console.log(userName, userRole)  // 'Алексей' 'admin'
    
    // Значение по умолчанию — если поля нет или оно undefined
    const { city, country = 'Россия' } = user
    console.log(city, country)  // 'Москва' 'Россия'
    
    // Вложенные объекты
    const response = { data: { user: { email: 'alex@mail.ru' } } }
    const { data: { user: { email } } } = response
    console.log(email)  // 'alex@mail.ru'
    
    // Остаток через ...rest
    const { name: n, ...rest } = user
    console.log(rest)  // { age: 28, role: 'admin', city: 'Москва' }

    Деструктуризация массивов

    const colors = ['red', 'green', 'blue', 'yellow']
    
    // Базовое — позиции важны, не имена
    const [first, second] = colors
    console.log(first, second)  // 'red' 'green'
    
    // Пропуск элементов — запятые без переменной
    const [, , third] = colors
    console.log(third)  // 'blue'
    
    // Значение по умолчанию
    const [a, b, c, d, e = 'white'] = colors
    console.log(e)  // 'white' — пятого элемента нет
    
    // Остаток
    const [head, ...tail] = colors
    console.log(head)  // 'red'
    console.log(tail)  // ['green', 'blue', 'yellow']
    
    // Обмен переменных без temp
    let x = 1, y = 2
    ;[x, y] = [y, x]
    console.log(x, y)  // 2 1

    В параметрах функции

    Очень часто в реальном коде — самодокументирующие параметры с дефолтами:

    // Вместо function createUser(name, role, theme, fontSize)
    function createUser({ name, role = 'user', theme = 'dark', fontSize = 14 } = {}) {
      console.log(`Создан: ${name} [${role}]`)
    }
    
    createUser({ name: 'Иван' })
    // Создан: Иван [user]
    
    createUser({ name: 'Мария', role: 'admin', theme: 'light' })
    // Создан: Мария [admin]

    Типичные ошибки

    Ошибка 1: деструктуризация null/undefined

    // Сломано:
    const { name } = null   // TypeError: Cannot destructure property 'name' of null
    
    // Исправлено — значение по умолчанию для всего объекта:
    const { name } = null ?? {}         // name = undefined
    const { name = 'Гость' } = null ?? {}  // name = 'Гость'

    Ошибка 2: путаница переименования и значения по умолчанию

    // Это переименование: новая переменная называется 'userName'
    const { name: userName } = user
    
    // Это значение по умолчанию:
    const { name = 'Гость' } = user
    
    // Оба вместе:
    const { name: userName = 'Гость' } = user

    Ошибка 3: деструктуризация в цикле — повторное объявление

    // Сломано:
    for (const item of items) {
      const { name } = item  // OK — const создаётся заново в каждой итерации
    }
    
    // Но вне цикла нельзя объявить дважды:
    const { name } = item1
    const { name } = item2  // SyntaxError: Identifier 'name' has already been declared
    
    // Исправлено — переименование:
    const { name: name1 } = item1
    const { name: name2 } = item2

    В реальных проектах

  • React: const { data, loading, error } = useQuery()
  • Node.js: const { PORT = 3000, DB_URL } = process.env
  • API-ответы: const { results: products, total, page } = await fetchProducts()
  • Импорты: import { useState, useEffect } from 'react' — тоже деструктуризация
  • Примеры

    Обработка ответа платёжного API (Stripe-подобный)

    // Симуляция ответа платёжного API
    const paymentResponse = {
      id: 'pay_3N8abc',
      status: 'succeeded',
      amount: 149900,  // в копейках
      currency: 'rub',
      customer: {
        id: 'cus_X7zZ',
        email: 'buyer@example.com',
        name: 'Алексей Смирнов',
        address: {
          city: 'Москва',
          street: 'ул. Тверская, 1',
        }
      },
      metadata: {
        orderId: 'ORD-2024-001',
        promoCode: 'WINTER20',
      },
      fees: [150, 75, 25]
    }
    
    // Деструктуризация вложенного объекта
    const {
      id: paymentId,
      status,
      amount,
      currency,
      customer: {
        email,
        name: customerName,
        address: { city }
      },
      metadata: { orderId, promoCode = null },
      fees: [mainFee, ...otherFees],
    } = paymentResponse
    
    console.log(paymentId)    // 'pay_3N8abc'
    console.log(status)       // 'succeeded'
    console.log(customerName) // 'Алексей Смирнов'
    console.log(city)         // 'Москва'
    console.log(orderId)      // 'ORD-2024-001'
    console.log(promoCode)    // 'WINTER20'
    console.log(mainFee)      // 150
    console.log(otherFees)    // [75, 25]
    console.log((amount / 100).toFixed(2) + ' ' + currency)  // '1499.00 rub'
    
    // Деструктуризация в параметрах функции
    function sendReceipt({ email, name: customerName, address: { city } = {} }) {
      console.log(`Квитанция отправлена ${customerName} (${email}) из ${city}`)
    }
    
    sendReceipt(paymentResponse.customer)
    // Квитанция отправлена Алексей Смирнов (buyer@example.com) из Москва

    Задание

    Пришёл ответ от API погоды. Напиши функцию formatWeather(data), которая деструктурирует объект data и возвращает отформатированную строку. Функция должна брать: город (city), температуру (temp), ощущаемую температуру (feelsLike из поля feels_like), описание (первый элемент массива weather), скорость ветра (wind.speed). Если описание отсутствует — использовать 'нет данных'.

    Подсказка

    Для первого элемента массива weather с дефолтом: const [{ description } = {}] = data.weather. Если массив пуст — деструктуризация даст undefined, тогда description ?? "нет данных".

    Загружаем среду выполнения...
    Загружаем AI-помощника...