Опціональний ланцюжок ?. – це безпечний спосіб доступу до вкладених властивостей об’єктів, навіть якщо проміжних властивостей не існує.
Проблема “відсутньої властивості”
Якщо ви тільки почали читати підручник і вивчати JavaScript, можливо ця проблема вам наразі незнайома, проте вона достатньо розповсюджена.
Наприклад, розглянемо об’єкт user який містить інформацію про наших користувачів.
В більшості наших користувачів є адреса user.address з вулицею user.address.street, проте дехто вирішив взагалі не вказувати адресу.
Отож якщо користувач не вказав адресу, а ми своєю чергою спробуємо отримати доступ до властивості user.address.street, то отримаємо помилку.
Це очікуваний розвиток подій, так працює JavaScript. Оскільки user.address є undefined, то і спроба отримати user.address.street закінчується помилкою.
Проте в багатьох життєвих ситуаціях було б набагато зручніше просто отримати undefined, що буде означати “немає вулиці”.
…Ще один приклад. У веброзробці ми можемо отримати об’єкт котрий відповідає елементу на вебсторінці за допомогою спеціальних методів, наприклад: document.querySelector('.elem'). Проте якщо ми намагатимемось отримати елемент, якого немає на сторінці, то нам вернеться null.
Для закріплення. Якщо елемента немає на вебсторінці, ми отримаємо помилку при спробі доступу до властивості .innerHTML від null. І в деяких випадках, коли відсутність елемента для нас є нормою, ми хотіли б просто отримати .innerHTML = null (тобто html = null).
Як ми можемо це реалізувати?
Найочевиднішим рішенням було б перевірити властивість використавши if або за допомогою умовного оператора ?:
Варіант робочий, помилки не буде… Але виглядає це вкрай неелегантно. Як ви бачите "user.address" двічі з’являється в коді.
Ось як те ж саме виглядає для document.querySelector:
Ми бачимо, що пошук елемента document.querySelector('.elem') насправді викликається тут двічі. Не добре.
Для властивостей які лежать глибше, це стає проблемою оскільки потребує більшого дублювання.
Спробуймо отримати доступ до user.address.street.name.
Виглядає просто жахливо та незрозуміло.
Але не хвилюйтесь, існує кращий варіант реалізації такої задачі за допомогою логічного оператора &&:
Логічне “І” з ланцюжком властивостей гарантує нам, що всі вони існують (якщо ж ні – обчислення припиняється), але й це все ще не ідеал.
Як ви бачите, імена властивостей досі дублюються в коді. В прикладі вище властивість user.address з’являється тричі.
Ось чому опціональний ланцюжок ?. був доданий в мову. Щоб розв’язати цю проблему раз і назавжди!
Опціональний ланцюжок
Опціональний ланцюжок ?. припиняє обчислення, якщо значення перед ?. є undefined або null, і повертає undefined.
Для стислості надалі в цій статті ми будемо говорити про значення, що воно “існує”, якщо воно відрізняється від null чи undefined
Іншими словами, value?.prop:
- працює як value.prop, якщо value існує,
- інакше (коли value є undefined/null) воно повертає undefined.
Ось безпечний спосіб доступу до властивості user.address.street за допомогою ?.:
Такий код виглядає коротшим і чистішим, взагалі немає ніякого дублювання.
Ось приклад із document.querySelector:
Читання властивості “address” з user?.address спрацює навіть коли в змінній user зберігається зовсім не об’єкт:
Зверніть увагу, що синтаксис ?. робить необов’язковою тільки властивість перед ним, а не будь-яку наступну.
Наприклад в user?.address.street.name конструкція user?. дозволяє user залишатись null/undefined (і повертати undefined в такому випадку), але це працює тільки для user. Доступ до решти властивостей здійснюється звичайним способом. Якщо ми хочемо, щоб якась з них була необов’язковою, тоді конкретно для цієї властивості нам доведеться замінити . на ?..
Нам слід використовувати ?. тільки в тих ситуаціях коли ми припускаємо, що значення може не існувати.
Наприклад, якщо за нашою логікою об’єкт user точно існує, але його властивість address є необов’язковою, тоді нам слід використовувати конструкцію user.address?.street. Проте аж ніяк не user?.address?.street.
Тоді якщо помилково змінна user виявиться пустою, ми побачимо програмну помилку і зможемо це виправити. В іншому випадку, якщо ми зловживаємо ?., помилки можуть замовчуватися там де це непотрібно й ускладнювати процес налагодження.
Якщо змінної user взагалі не існує, тоді конструкція user?.anything видасть помилку:
Змінна обов’язково повинна бути оголошена (наприклад let/const/var user або як параметр функції). Опціональний ланцюжок працює тільки з чинними змінними.
Скорочене обчислення
Як вже говорилось, ?. негайно припиняє обчислення, якщо лівої частини не існує.
Таким чином, якщо є додаткові виклики функцій або операції праворуч від ?., вони не будуть виконані.
Наприклад:
Інші способи застосування: ?.(), ?.[]
Опціональний ланцюжок ?. – це не оператор, а спеціальна синтаксична конструкція, що також працює з функціями та квадратними дужками.
Наприклад, ?.() використовується для виклику потенційно відсутньої функції.
В прикладі нижче не в усіх користувачів є метод admin:
В обох випадках спочатку використовуємо крапку (userAdmin.admin) для доступу до властивості admin, оскільки об’єкт користувача точно існує, а це означає, що ми можемо звернутись до будь-якої його властивості.
Вже потім ?.() перевіряє ліву частину: якщо функція admin існує, то вона виконається (у випадку з userAdmin). Інакше (для userGuest) обчислення припиниться без помилок.
Також існує синтаксис ?.[], якщо ми хочемо отримати доступ до властивості за допомогою квадратних дужок [], а не через крапку .. Як і в решті випадків, такий спосіб дає змогу безпечно читати властивості об’єкта яких може не існувати.
Ми також можемо використовувати ?. з delete:
Опціональний ланцюжок ?. не має сенсу у лівій частині присвоювання.
Наприклад:
Воно недостатньо «розумне» для цього.
Підсумки
Синтаксис опціонального ланцюжка ?. має три форми:
- obj?.prop – повертає obj.prop, якщо існує obj, і undefined в іншому випадку.
- obj?.[prop] – повертає obj[prop], якщо існує obj, і undefined в іншому випадку.
- obj.method?.() – викликає obj.method(), якщо існує obj.method, в іншому випадку повертає undefined.
Як бачимо, всі вони прості та зрозумілі в використанні. ?. перевіряє ліву частину на рівність null/undefined і дозволяє продовжувати обчислення якщо це не так.
Ланцюжок ?. дозволяє без виникнення помилок звертатись до вкладених властивостей.
Однак, потрібно розумно застосовувати ?., тільки в тих випадках де допустимо, що ліва частина не існує. Щоб таким чином не приховувати потенційні помилки програмування.
Коментарі