Введение в 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;
Более подробную информацию можно найти в разделе «Семантика».