7 советов по обработке undefined в JavaScript
undefined в JavaScript — что за значение, чем вызвано и как его обработать? Также рассмотрим главные отличия undefined и null.
142К открытий144К показов
undefined в JavaScript — настоящий кошмар начинающего фронтендера. В чём отличие undefined и null
, если даже сравнение null == undefined
выводит true
, и как обрабатывать ошибку undefined? Давайте разбираться.
Примечание Вы читаете улучшенную версию некогда выпущенной нами статьи.
Умеете обрабатывать undefined в JavaScript?
Конечно: пользуюсь перечисленными в статье методами
Да, но помимо перечисленных знаю ещё кое-какие способы
Нет, но собираюсь разобраться
- Что такое undefined в JavaScript
- Как избежать undefined в JavaScript
- Значение undefined в массивах
- Отличие null и undefined в JavaScript
- Заключение
Что такое undefined в JavaScript
undefined является специальным значением. Согласно спецификации ECMAScript, undefined в JavaScript можно получить при доступе к неинициализированным переменным, несуществующим свойствам объекта, несуществующим элементам массива, etc. Пример:
Как видим, undefined выводится при попытке доступа к:
- неинициализированной переменной
number
; - несуществующему свойству объекта
movie.year
; - несуществующему элементу массива
movies[3]
.
Оператор typeof
возвращает строку undefined для неопределённого значения:
Оператор typeof
отлично подходит для проверки значения undefined в JavaScript:
Как избежать undefined в JavaScript
Неинициализированная переменная
Объявленная переменная, которая еще не имеет значения (не инициализирована), по умолчанию undefined . Пример:
Переменная myVariable
уже объявлена, но ещё не имеет присвоенного значения. Попытка обратиться к ней закончится выводом undefined . Чтобы это исправить, достаточно присвоить переменной значение. Чем меньше переменная существует в неинициализированном состоянии, тем лучше.
Ниже приведены способы решения проблемы.
1. const и let вместо var
Объявленные таким образом объекты и переменные находятся в области видимости, ограниченной текущим блоком кода, и находятся во временной мёртвой зоне до момента присвоения им значения.
При использовании неизменяемых данных (констант) рекомендуется инициализировать их как const
:
Константа не подвергается неинициализированному состоянию, и получить значение undefined в этом случае невозможно.
Если вам нужно менять значение переменной, то обозначьте её как let
и также присваивайте ей начальное значение:
Проблема var
заключается в поднятии переменных: где бы ни находилось объявление, это равнозначно тому, что переменную объявили в начале кода.
В этом случае переменная myVariable
содержит undefined до получения значения:
Если же переменную объявить как let
, она останется недоступной до момента присвоения ей значения. Таким образом, использование const
или let
снизит риск получения значения undefined в JavaScript.
2. Усиление связности
Связность характеризует степень взаимосвязи элементов модуля (пространства имён, класса, метода, блока кода). Сильная связность предпочтительнее, поскольку предполагает, что элементы модуля должны фокусироваться исключительно на одной задаче. Это поможет модулю быть:
- сфокусированным и понятным;
- легко поддерживаемым и поддающимся рефакторингу;
- многоразовым;
- простым для тестирования.
Блок кода сам по себе может считаться небольшим модулем. Чтобы извлечь выгоду из преимуществ сильной связности, нужно держать переменные как можно ближе к блоку кода, который их использует.
Вот классический пример того, как не надо делать:
index
, item
и length
объявляются в начале функции, но используются они лишь ближе к концу. Всё время между объявлением переменной в начале и до использования её в цикле index
и item
не инициализируются и выводят undefined . Разумнее переместить переменные ближе к месту их применения:
Доступ к несуществующему свойству
Попытка получить доступ к несуществующему свойству объекта JavaScript заканчивается undefined . Пример:
favoriteMovie
— объект с одним значением title
. Доступ к несуществующему свойству actors
приведёт к выводу undefined .
Сам по себе доступ к не вызовет ошибку, а вот при попытке получить значение из несуществующего свойства выведется ошибка:
Проблема в особенностях JavaScript: свойство может быть установлено или отсутствовать. Хорошее решение — установка правил, которые обязывают задать свойствам значения.
Но не всегда возможно контролировать объекты, с которыми приходится работать. Такие объекты могут иметь разный набор свойств в различных сценариях, и каждый из них нужно обрабатывать вручную.
Реализуем функцию append(array, toAppend)
, которая добавляет в начале и/или в конце массива новые элементы:
Поскольку объект toAppend может удалять первые или последние свойства, необходимо проверить их существование с помощью условий if(toAppend.first){} и if(toAppend.last){}.
Вот только undefined , как false, null, 0, NaN и ‘ ‘, является ложными значением, а в текущей реализации функция append() не позволяет вставлять ложные элементы:
0 и false — ложные значения, потому что if (toAppend.first){} и if (toAppend.last){} фактически сравниваются с ложными значениями, и эти элементы не вставляются в массив. Функция возвращает исходный массив [10] без изменений.
1. Наличие свойства
К счастью, JavaScript предлагает множество способов определить, имеет ли объект определённое свойство:
- obj.prop !== undefined в JavaScript позволяет проверить undefined , сравнив с ним объект;
- typeof obj.prop !== ‘undefined’ проверяет тип значения свойства;
- obj.hasOwnProperty(‘prop’) проверяет объект на наличие собственного свойства;
- ‘prop’ in obj проверяет объект на наличие собственного или унаследованного свойства.
Рекомендацией в этом случае будет использование оператора in , чтобы проверить, имеет ли объект определённое свойство, не обращаясь к фактическому значению этого свойства:
first in toAppend ( как и last in toAppend) выводит true, независимо от существующего свойства. В других случаях выводится — false.
Использование оператора in устраняет проблему со вставкой ложных элементов 0 и false. Теперь добавление элементов в начале и в конце массива [10] приводит к ожидаемому результату: [0, 10, false].
2. Деструктуризация доступа к свойствам объекта
Деструктуризация объекта позволяет устанавливать значение по умолчанию, если свойство не существует: удобно для исключения прямого контакта с undefined :
Применяя преимущества деструктуризации объекта, реализуем quote():
const { char = ‘”‘, skipIfQuoted = true } = config в одной строкe извлекает свойства char и skipIfQuoted из объекта config.
Если некоторые свойства недоступны в объекте config, деструктуризация задаёт значения по умолчанию: ‘”‘ для char и false для skipIfQuoted. К счастью, функцию можно улучшить.
Переместим деструктуризацию в раздел параметров и установим значение по умолчанию (пустой объект { }) для параметра config, чтобы пропустить второй аргумент, когда будет достаточно значений по умолчанию:
Деструктурирующее присваивание гарантирует, что используется пустой объект, если второй аргумент не указан вообще. В результате вы избегаете возникновения значения undefined в JavaScript.
3. Свойство по умолчанию
Есть простой способ установить значения по умолчанию для свойств объекта, и имя ему Spread syntax:
Инициализатор объекта распространяет свойства из исходных объектов defaults и unsafeOptions. Важен порядок, в котором указаны исходные объекты: свойства более позднего исходного объекта перезаписывают более ранние. Независимо от ситуации, объект всегда содержит полный набор свойств, и появление undefined невозможно.
Параметры функции
Функция, имеющая определённые параметры, должна вызываться с одинаковым количеством аргументов. В таком случае параметры получают ожидаемые значения:
При вызове функции multiply(5, 3) параметры a и b получают соответствующие значения 5 и 3. Умножение рассчитывается как ожидаемое: 5 * 3 = 15.
Но что происходит, когда пропускается аргумент при вызове? Параметр внутри функции получает значение undefined . Как этого избежать?
Лучшим подходом является использование параметров по умолчанию из ES2015:
Значение b = 2 в сигнатуре функции гарантирует, что если b получит значение undefined , то по умолчанию параметр изменится на 2.
Возвращаемое значение функции
В JavaScript функция, которая не имеет оператора return, возвращает значение undefined :
То же происходит, если return присутствует, но без какого-либо выражения рядом:
Указывая значение для return, можно получить желаемый результат:
Теперь вызов функции выведет нужное значение.
Оператор void
Оператор void выполняет выражение и возвращает undefined вне зависимости от результата:
Одним из вариантов использования оператора void является переопределение результата выполнения выражения и возврат undefined в случае возникновения неожиданных результатов выполнения функции.
Значение undefined в массивах
Вы получаете undefined при попытке доступа к элементу массива с индексом вне пределов массива.
Массив colors имеет 3 элемента, поэтому корректные индексы равны 0, 1 и 2. Поскольку в индексах массива 5 и -1 нет элементов, значения colors[5] и colors[-1] получают значение undefined .
В JavaScript вы можете столкнуться с так называемыми разрежёнными массивами. Эти массивы имеют пробелы, то есть на некоторых индексах не определены никакие элементы. Когда делаем попытку получить доступ к пустому значению в разрежённом массиве, на выходе получаем undefined :
sparse1 создается путем вызова конструктора Array с числовым первым аргументом. Он имеет 3 пустых элемента. sparse2 создается с литералом массива, второй элемент которого отсутствует. В любом из этих массивов доступ к пустому элементу оценивается как undefined .
Отличие null и undefined в JavaScript
Основное отличие в том, что undefined представляет значение переменной, которая ещё не была инициализирована, а null — намеренное отсутствие объекта.
Допустим, переменная number определена, но ей не назначено начальное значение:
То же самое произойдёт при попытке доступа к несуществующему свойству объекта:
Или переменная должна ожидать возвращение объекта функции, но по какой-то причине создание объекта невозможно. В этом случае null является значимым индикатором недостающего объекта. Например, clone() — это функция, которая клонирует простой объект JavaScript. Ожидается, что функция вернёт объект:
Но clone() может быть вызван с пустым аргументом: 15 или null. В этом случае функция не может создать клон, поэтому возвращает null — индикатор отсутствующего объекта.
В JavaScript существуют проверки на null и undefined . Оператор typeof демонстрирует различие между двумя значениями:
Строгий оператор равенства === также отличает undefined от null:
Вам также может быть интересна наша статья про обработку ошибок в JavaScript.
Заключение
Стратегия борьбы с undefined в JavaScript:
- Уменьшить использование неинициализированных переменных.
- Сделать жизненный цикл переменных коротким и близким к источнику их использования.
- Назначить начальное значение переменным.
- Использовать const или let.
- Проверять наличие свойств или заполнить небезопасные объекты по умолчанию.
- Использовать значения по умолчанию для некритичных параметров функции.
- Избегать использования разрежённых массивов.
142К открытий144К показов