Недавнее дополнение
Это недавнее дополнение к языку. Старым браузерам могут потребоваться полифилы.
Необязательная цепочка ?.
— это безопасный способ доступа к свойствам вложенных объектов, даже если промежуточное свойство не существует.
Если вы только начали читать учебник и изучать JavaScript, возможно, проблема вас еще не коснулась, но она довольно распространена.
В качестве примера предположим, что у нас есть объекты user
, которые содержат информацию о наших пользователях.
У большинства наших пользователей есть адреса в свойстве user.address
с указанием улицы user.address.street
, но некоторые их не предоставили.
В таком случае, когда мы пытаемся получить user.address.street
, а у пользователя нет адреса, мы получаем ошибку:
пусть пользователь = {}; // пользователь без свойства "адрес" оповещение(пользователь.адрес.улица); // Ошибка!
Это ожидаемый результат. JavaScript работает следующим образом. Поскольку user.address
undefined
, попытка получить user.address.street
завершается ошибкой.
Во многих практических случаях мы бы предпочли получить здесь undefined
вместо ошибки (что означает «нет улицы»).
… и еще один пример. В веб-разработке мы можем получить объект, соответствующий элементу веб-страницы, с помощью вызова специального метода, например document.querySelector('.elem')
, и он возвращает null
если такого элемента нет.
// document.querySelector('.elem') имеет значение null, если элемента нет пусть html = document.querySelector('.elem').innerHTML; // ошибка, если оно равно нулю
Еще раз: если элемент не существует, мы получим ошибку при доступе к свойству .innerHTML
имеющему значение null
. А в некоторых случаях, когда отсутствие элемента является нормальным, мы бы хотели избежать ошибки и просто принять в качестве результата html = null
.
Как мы можем это сделать?
Очевидным решением было бы проверить значение с помощью условного оператора if
или ?
, прежде чем получить доступ к его свойству, например:
пусть пользователь = {}; оповещение (user.address ? user.address.street: не определено);
Работает, ошибок нет... Но это довольно неэлегантно. Как видите, "user.address"
появляется в коде дважды.
Вот как то же самое будет выглядеть для document.querySelector
:
пусть html = document.querySelector('.elem')? document.querySelector('.elem').innerHTML: null;
Мы видим, что поиск элемента document.querySelector('.elem')
фактически вызывается здесь дважды. Не хорошо.
Для более глубоко вложенных свойств ситуация становится еще уродливее, поскольку требуется больше повторений.
Например, давайте аналогичным образом получим user.address.street.name
.
пусть пользователь = {}; // у пользователя нет адреса alert(user.address ? user.address.street ? user.address.street.name : null : null);
Это просто ужасно, с пониманием такого кода могут даже возникнуть проблемы.
Есть немного лучший способ написать это, используя оператор &&
:
пусть пользователь = {}; // у пользователя нет адреса alert(user.address && user.address.street && user.address.street.name ); // неопределенно (нет ошибок)
Использование AND для всего пути к свойству гарантирует, что все компоненты существуют (в противном случае вычисление прекращается), но это также не идеально.
Как видите, имена свойств по-прежнему дублируются в коде. Например, в приведенном выше коде user.address
появляется три раза.
Вот почему необязательная цепочка ?.
был добавлен в язык. Чтобы решить эту проблему раз и навсегда!
Необязательная цепочка ?.
останавливает оценку, если значение перед ?.
имеет undefined
или null
значение и возвращает undefined
.
Далее в этой статье для краткости мы будем говорить, что что-то «существует», если оно не null
и не undefined
.
Другими словами, value?.prop
:
работает как value.prop
, если value
существует,
в противном случае (когда value
undefined/null
) возвращается undefined
.
Вот безопасный способ получить доступ к user.address.street
с помощью ?.
:
пусть пользователь = {}; // у пользователя нет адреса оповещение(пользователь?.адрес?.улица); // неопределенно (нет ошибок)
Код короткий и понятный, дублирования вообще нет.
Вот пример с document.querySelector
:
пусть html = document.querySelector('.elem')?.innerHTML; // будет неопределенным, если элемента нет
Чтение адреса с помощью user?.address
работает, даже если объект user
не существует:
пусть пользователь = ноль; предупреждение(пользователь?.адрес); // неопределенный оповещение(пользователь?.адрес.улица); // неопределенный
Обратите внимание: ?.
синтаксис делает необязательным значение перед ним, но не дальше.
Например, в user?.address.street.name
?.
позволяет user
безопасно быть null/undefined
(и в этом случае возвращает undefined
), но это только для user
. Доступ к остальным свойствам осуществляется обычным способом. Если мы хотим, чтобы некоторые из них были необязательными, нам нужно будет заменить другие .
с ?.
.
Не злоупотребляйте необязательной цепочкой
Нам следует использовать ?.
только там, где нормально, что чего-то не существует.
Например, если по логике нашего кода user
объект должен существовать, но address
не является обязательным, то мы должны писать user.address?.street
, а не user?.address?.street
.
Затем, если user
не определен, мы увидим программную ошибку и исправим ее. В противном случае, если мы злоупотребим ?.
ошибки кодирования могут быть скрыты там, где это нецелесообразно, и их становится сложнее отлаживать.
Переменная перед ?.
должен быть объявлен
Если переменной user
вообще нет, то user?.anything
вызывает ошибку:
// Ошибка ссылки: пользователь не определен пользователь?.адрес;
Переменная должна быть объявлена (например, let/const/var user
или как параметр функции). Необязательное связывание работает только для объявленных переменных.
Как было сказано ранее, ?.
немедленно останавливает («замыкает») вычисление, если левая часть не существует.
Итак, если справа от ?.
, они не будут сделаны.
Например:
пусть пользователь = ноль; пусть х = 0; пользователь?.sayHi(x++); // нет "пользователя", поэтому выполнение не доходит до вызова SayHi и x++ предупреждение (х); // 0, значение не увеличивается
Необязательная цепочка ?.
— это не оператор, а специальная синтаксическая конструкция, которая также работает с функциями и квадратными скобками.
Например, ?.()
используется для вызова функции, которая может не существовать.
В приведенном ниже коде у некоторых наших пользователей есть метод admin
, а у некоторых нет:
пусть userAdmin = { администратор() { alert("Я администратор"); } }; пусть userGuest = {}; userAdmin.admin?.(); // Я администратор userGuest.admin?.(); // ничего не происходит (нет такого метода)
Здесь в обеих строках мы сначала используем точку ( userAdmin.admin
), чтобы получить свойство admin
, поскольку мы предполагаем, что объект user
существует и его можно безопасно прочитать из него.
Затем ?.()
проверяет левую часть: если функция admin
существует, то она запускается (это касается userAdmin
). В противном случае (для userGuest
) оценка останавливается без ошибок.
Синтаксис ?.[]
также работает, если мы хотим использовать скобки []
для доступа к свойствам вместо точки .
. Как и в предыдущих случаях, он позволяет безопасно прочитать свойство объекта, который может не существовать.
пусть ключ = "первое имя"; пусть пользователь1 = { Имя: «Джон» }; пусть user2 = ноль; предупреждение(пользователь1?.[ключ]); // Джон предупреждение(пользователь2?.[ключ]); // неопределенный
Также мы можем использовать ?.
с delete
:
удалить пользователя?.имя; // удаляем user.name, если пользователь существует
Мы можем использовать ?.
для безопасного чтения и удаления, но не записи
Необязательная цепочка ?.
не имеет смысла в левой части задания.
Например:
пусть пользователь = ноль; пользователь?.name = "Джон"; // Ошибка, не работает // потому что его результат равен: undefined = "Джон"
Необязательная цепочка ?.
синтаксис имеет три формы:
obj?.prop
– возвращает obj.prop
, если obj
существует, в противном случае — undefined
.
obj?.[prop]
– возвращает obj[prop]
если obj
существует, в противном случае — undefined
.
obj.method?.()
– вызывает obj.method()
если obj.method
существует, в противном случае возвращает undefined
.
Как мы видим, все они понятны и просты в использовании. ?.
проверяет левую часть на null/undefined
и позволяет продолжить оценку, если это не так.
Цепочка ?.
позволяет безопасно получать доступ к вложенным свойствам.
Тем не менее, нам следует подать заявку ?.
осторожно, только там, где согласно логике нашего кода допустимо отсутствие левой части. Чтобы он не скрывал от нас ошибок программирования, если они возникнут.