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

Во-первых, конечно, бывают случаи, когда оператор точки вполне подходит. Знать где и почему - вот о чем эта статья. Но в основном это касается деструктурирования, которое, как я считаю, является недооцененным и недостаточно используемым решением.

Деструктуризация недооценена и сильно недооценена.

Во-вторых, давайте с самого начала признаем, что в ES2019 в JavaScript были введены два новых оператора:

  • Необязательная цепочка: ?.
  • Нулевое слияние: ??

Они позволяют записывать user?.name ?? 'Noname' и не давать сбой для несуществующего user и иметь запасной вариант на случай, если какой-либо из user или name не существует.

Это нормально, но не так хорошо, как деструктуризация.

Эй…

Начнем с самого начала

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

const getTemperature = (atticData) => atticData.celsius || 15;

У этой функции будут две серьезные проблемы:

  • Что, если объект, который вы передаете функции, в какой-то момент, undefined, неинициализирован? Тогда он рухнет.
  • Что делать, если температура по Цельсию равна 0? Функция вернет 15!

С помощью двух новых операторов, упомянутых в начале, вы исправляете функцию:

const getTemperature = (atticData) => atticData?.celsius ?? 15;

Две проблемы решены. Отлично.

Должны ли мы теперь везде использовать необязательный оператор цепочки?

Нет. По двум причинам:

  • Это просто затруднило бы чтение любой известной статической структуры, например API. Например. JSON.parse, window.localStorage.getItem или идентификаторы внутри импортированных библиотек.
  • Уничтожение может быть лучшей альтернативой в большинстве других случаев, когда мы обрабатываем любые данные.

Продолжая наш пример функции выше - она, написанная с помощью деструктурирования, будет выглядеть так:

const getTemperature = ({ celsius = 15 } = {}) => celsius;

Он немного чище и на самом деле стал более общим, поскольку не обязательно лжет о том, над чем работает. Мы избегаем этого называть. Этой функции можно передать любой потенциальный объект, и она попытается безопасно извлечь celsius.

Другой способ подумать об этом заключается в том, что внутри нашей функции нам никогда не нужно ссылаться на объект данных, а только на вещи внутри него. Поэтому мы выясняем это как можно раньше. Тело функции также может не делать ничего ни с чем, кроме celsius, например, у него не может возникнуть безумная идея изменить объект данных - он недоступен.

Уничтожьте как можно раньше.

(Версия деструктуризации не совсем такая же. См. раздел о null last в статье.)

Изменение привычек

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

Часто это позволяет сократить код с более высоким контролем. В основном из-за возможности устанавливать значения по умолчанию, но также потому, что мы можем контролировать, что доступно в области действия и как это доступно. У нас также есть возможность деструктурирования на нескольких уровнях, «вложенного деструктурирования», а затем по ходу установки устанавливать безопасные значения по умолчанию.

Как уже упоминалось, деструктурирование не требуется для известных статических API. Но любые данные даже некоторая конфигурация, которую вы считаете статической, заслуживают деструктурирования со значениями по умолчанию.

Использование деструктуризации

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

Эта функция по-разному использует возможности деструктуризации. Давайте проанализируем:

  • Установка значений по умолчанию на первом / высшем уровне (список аргументов) гарантирует, что мы не сможем завершить работу при пустых аргументах: func(). (Это «параметры по умолчанию», которые прекрасно работают так же, как и параметры по умолчанию при деструктуризации).
  • Переименуйте длинные имена ключей для более удобочитаемой логики при использовании значения (msg).
  • Используйте вложенную деструктуризацию, если вам нужно только то, что внутри значения члена. Но опять же, не забудьте также добавить значение по умолчанию, чтобы обезопасить деструктуризацию от сбоя, если значение элемента не существует.
  • Также полезно использовать оператор rest, чтобы иметь возможность обрабатывать все, что не было явно деструктурировано.
  • Если нам обоим необходимо использовать внутри всю структуру и значения, мы деструктурируем в области действия функции (в противном случае в заголовке функции - уже возможный).
  • И деструктуризация, и оператор остатка также могут использоваться для массивов.
  • Нам не нужно повторять длинное выражение options.pos.x для значения, если оно используется в области видимости несколько раз. Вместо этого просто x. Более чистый, безопасный и более производительный, поскольку JS не нужно каждый раз выкапывать значение.

Это был пример подготовки к…

Тело функции

Учтите, что вам в теле какой-то функции нужно:

profile.user.address.street.name
profile.user.address.city

Скажем, это очень динамические данные, и вы хотите что-то с ними сделать, возможно, объединить. Без деструктуризации вы наберете:

`${profile?.user?.address?.street?.name ?? “Unknown street”} ${profile?.user?.address?.city ?? “Unknown city”}`

Не очень приятно (?) Это смесь:

  • Чем мы занимаемся
  • Как мы с этим работаем

С деструктуризацией, сделанной как можно раньше в соответствии с ситуацией:

  • Вы декларативно выполнили распаковку всего, что важно для логики, на отдельном этапе.
  • Если вы выполнили деструктуризацию в списке аргументов функции, доступны только значения, которые необходимы для логики, больше ничего. Это также добавляет того, почему деструктуризация может считаться более безопасной. Вы контролируете то, что находится в поле зрения.
  • Вы знаете, что у вас есть безопасные, короткие значения, с которыми можно работать, а также легко использовать несколько раз в области действия. Это положительно для ясности логики.

Итак, в теле функции вы набираете:

`${streetName} ${city}`

Заключение

Что эта статья предлагает как привычку:

  • Каждый раз вы вводите точку . - стоп и думаете, не ли это данные, с которыми вы работаете. Если да, то используйте деструктуризацию.
  • Уничтожьте как можно раньше.
  • Не забудьте включить значения по умолчанию, особенно при вложенной деструктуризации.

Я знаю ... Мы должны обратиться к слону в комнате:

Null в JavaScript

Версия небольшого примера с деструктуризацией не совсем аналогична необязательному связыванию и слиянию с нулевым значением. null - это значение, которое будет вести себя иначе.

Но это не строго деструктуризация, которая вызывает ошибку в примере, это параметры по умолчанию (введенные в ES2015 / ES6). Некоторые говорят об этом так:

null нарушает параметры по умолчанию.

Однако значения по умолчанию для деструктуризации работают таким же образом, что означает, что они также возвращаются к значениям по умолчанию только для undefined - не для null.

На самом деле это потому, что это правильный путь

  • undefined в JavaScript означает, что значение еще не установлено, оно не определено. Все неинициализированные значения получают значение undefined во время выполнения.
  • null - это идентификатор, который означает, что значение действительно установлено, но по какой-то причине ему не удалось получить правильное значение. Исторически null был включен в язык, когда кто-то хотел очистить переменную, содержащую (ссылающуюся) на объект, и позволить сборке мусора уничтожить его. Поэтому его тип "object". null отличается от undefined и не означает одно и то же (и не должно быть в вашем коде).

undefined и null не означают одно и то же.

Признавая значения и различия между этими двумя, оба параметра по умолчанию и деструктуризация работают по назначению.

Я вместе с Дугласом Крокфордом, командой Typescript, Эриком Эллиотом и многими другими рекомендую просто не использовать в JavaScript null (всегда используйте undefined и используйте ESLint, чтобы избежать этого: https://www.npmjs.com/package/eslint-plugin-no-null).

null не следует использовать в JavaScript.

Но это тема для другой статьи.