Введение в optional chaining operator
Длинные цепочки доступа к свойствам в JavaScript могут быть подвержены ошибкам, так как любой из них может иметь значение null
или undefined
(также известный как «нулевые» значения). Проверка существования свойства на каждом шаге легко превращается в глубоко вложенную структуру if
-statements или длинное условие в if
, реплицирующее цепочку доступа к свойству:
const nameLength = db.user.name.length; let nameLength; if (db && db.user && db.user.name) nameLength = db.user.name.length;
Вышесказанное также можно выразить с помощью тернарного оператора, который не совсем помогает читабельности:
const nameLength = (db ? (db.user ? (db.user.name ? db.user.name.length : undefined) : undefined) : undefined);
Представляем опциональный оператор цепочки
Конечно, вы не хотите писать такой код, поэтому желательно иметь какую-то альтернативу. Некоторые другие языки предлагают элегантное решение этой проблемы с помощью функции, называемой «необязательное сцепление». Согласно недавнему предложению в спецификациях, «необязательная цепочка - это цепочка из одного или нескольких обращений к свойствам и вызовов функций, первый из которых начинается с токена ?.
».
Используя новый необязательный оператор связывания, мы можем переписать приведенный выше пример следующим образом:
const nameLength = db?.user?.name?.length;
Что происходит , когда db
, user
или name
равны undefined
или null
? С новым опциональным оператором цепочек, JavaScript инициализирует nameLength
как undefined
вместо того , чтобы бросать ошибку.
Обратите внимание, что это поведение также более надежно, чем наша проверка if (db && db.user && db.user.name)
. Например, что, если name
гарантированно будет строкой? Мы могли бы изменить name?.length
на name.length
. Тогда, если бы в name
была пустая строка, мы все равно получили бы правильную длину 0
. Это потому, что пустая строка является ложным значением: она ведет себя как false
в условиях if
. Необязательный оператор цепочки исправляет этот распространенный источник ошибок.
Дополнительные синтаксические формы: вызовы и динамические свойства
Также есть версия оператора для вызова дополнительных методов:
const adminOption = db?.user?.validateAdminAndGetPrefs?.().option;
Синтаксис может показаться неожиданным, так как ?.()
это фактический оператор, который применяется к выражению перед ним.
Существует третье использование оператора, а именно необязательный динамический доступ к свойству, который осуществляется через ?.[]
. Он либо возвращает значение, на которое ссылается аргумент в скобках, либо, undefined
если нет объекта для получения значения. Вот возможный вариант использования, следуя примеру выше:
const optionName = 'optional setting'; const optionLength = db?.user?.preferences?.[optionName].length;
Эта последняя форма также доступна для необязательного индексирования массивов, например:
const userIndex = 42; const userName = usersArray?.[userIndex].name;
Необязательный оператор связывания ??
может быть объединен с нулевым оператором объединения, когда требуется undefined
значение не по умолчанию. Это обеспечивает безопасный глубокий доступ к свойству с указанным значением по умолчанию, обращаясь к общему варианту _.get
использования, который ранее требовал пользовательских библиотек, таких как lodash:
const object = { id: 123, names: { first: 'Alice', last: 'Smith' }}; { // lodash: const firstName = _.get(object, 'names.first'); // → 'Alice' const middleName = _.get(object, 'names.middle', '(no middle name)'); // → '(no middle name)' } { // с новым оператором: const firstName = object?.names?.first ?? '(no first name)'; // → 'Alice' const middleName = object?.names?.middle ?? '(no middle name)'; // → '(no middle name)' }
Свойства необязательного оператора цепочки
Опциональный оператор связывания обладает несколькими интересными свойствами: короткое замыкание, стекирование и необязательное удаление. Давайте пройдемся по каждому из них на примере.
Короткое замыкание означает не оценку остальной части выражения, если дополнительный оператор цепочки возвращается рано:
db?.user?.grow(++age);
Стекирование означает, что к последовательности обращений к свойствам можно применить несколько необязательных операторов сцепления:
const firstNameLength = db.users?.[42]?.names.first.length;
Тем не менее, подумайте об использовании более одного необязательного оператора цепочки в одной цепочке. Если значение гарантированно не будет нулевым, то использование оператора ?.
для доступа к свойствам не рекомендуется. В приведенном выше примере, db
всегда можно определить, но db.users
и db.users[42]
может не существовать. Если в базе данных есть такой пользователь, names.first.length
предполагается, что он всегда определен.
Необязательное удаление означает, что оператор delete
может быть объединен с необязательной цепочкой:
delete db?.user;
Более подробную информацию можно найти в разделе «Семантика».