ÐпÑионалÑÐ½Ð°Ñ ÑепоÑка ?. â ÑÑо безопаÑнÑй ÑпоÑоб доÑÑÑпа к ÑвойÑÑвам вложеннÑÑ
обÑекÑов, даже еÑли какое-либо из пÑомежÑÑоÑнÑÑ
ÑвойÑÑв не ÑÑÑеÑÑвÑеÑ.
ÐÑоблема «неÑÑÑеÑÑвÑÑÑего ÑвойÑÑва»
ÐÑли Ð²Ñ ÑолÑко наÑали ÑиÑаÑÑ ÑÑебник и изÑÑаÑÑ JavaScript, Ñо, возможно, пÑоблема Ð²Ð°Ñ ÐµÑÑ Ð½Ðµ коÑнÑлаÑÑ, но она доволÑно ÑаÑпÑоÑÑÑанена.
РкаÑеÑÑве пÑимеÑа пÑедположим, ÑÑо Ñ Ð½Ð°Ñ ÐµÑÑÑ Ð¾Ð±ÑекÑÑ user, коÑоÑÑе ÑодеÑÐ¶Ð°Ñ Ð¸Ð½ÑоÑмаÑÐ¸Ñ Ð¾ наÑиÑ
полÑзоваÑелÑÑ
.
У болÑÑинÑÑва наÑиÑ
полÑзоваÑелей еÑÑÑ Ð°Ð´ÑеÑа в ÑвойÑÑве user.address Ñ ÑлиÑей user.address.street, но некоÑоÑÑе из ниÑ
иÑ
не Ñказали.
Ð Ñаком ÑлÑÑае, когда Ð¼Ñ Ð¿Ð¾Ð¿ÑÑаемÑÑ Ð¿Ð¾Ð»ÑÑиÑÑ user.address.street, а полÑзоваÑÐµÐ»Ñ Ð¾ÐºÐ°Ð¶ÐµÑÑÑ Ð±ÐµÐ· адÑеÑа, Ð¼Ñ Ð¿Ð¾Ð»ÑÑим оÑибкÑ:
let user = {}; // полÑзоваÑÐµÐ»Ñ Ð±ÐµÐ· ÑвойÑÑва "address"
alert(user.address.street); // ÐÑибка!
ÐÑо ожидаемÑй ÑезÑлÑÑаÑ. JavaScript ÑабоÑÐ°ÐµÑ ÑледÑÑÑим обÑазом. ÐоÑколÑÐºÑ user.address Ð¸Ð¼ÐµÐµÑ Ð·Ð½Ð°Ñение undefined, попÑÑка полÑÑиÑÑ user.address.street завеÑÑаеÑÑÑ Ð¾Ñибкой.
Ðо многиÑ
пÑакÑиÑеÑкиÑ
ÑлÑÑаÑÑ
Ð¼Ñ Ð±Ñ Ð¿ÑедпоÑли полÑÑиÑÑ Ð·Ð´ÐµÑÑ undefined вмеÑÑо оÑибки (ÑÑо ознаÑало Ð±Ñ Â«ÑлиÑÑ Ð½ÐµÑ»).
â¦Ðли еÑÑ Ð¾Ð´Ð¸Ð½ пÑимеÑ. Рвеб-ÑазÑабоÑке Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ полÑÑиÑÑ Ð¾Ð±ÑекÑ, ÑооÑвеÑÑÑвÑÑÑий ÑлеменÑÑ Ð²ÐµÐ±-ÑÑÑаниÑÑ, Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ ÑпеÑиалÑного вÑзова меÑода, Ñакого как document.querySelector('.elem'), и он возвÑаÑÐ°ÐµÑ null, когда Ñакого ÑлеменÑа неÑ.
// document.querySelector('.elem') Ñавен null, еÑли ÑлеменÑа неÑ
let html = document.querySelector('.elem').innerHTML; // оÑибка, еÑли он Ñавен null
ÐÑÑ Ñаз, еÑли ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð½Ðµ ÑÑÑеÑÑвÑеÑ, Ð¼Ñ Ð¿Ð¾Ð»ÑÑим ÑообÑение об оÑибке доÑÑÑпа к ÑвойÑÑÐ²Ñ .innerHTML Ñ null. Рв некоÑоÑÑÑ
ÑлÑÑаÑÑ
, когда оÑÑÑÑÑÑвие ÑлеменÑа ÑвлÑеÑÑÑ Ð½Ð¾ÑмалÑнÑм, Ð¼Ñ Ñ
оÑели Ð±Ñ Ð¸Ð·Ð±ÐµÐ¶Ð°ÑÑ Ð¾Ñибки и пÑоÑÑо пÑинÑÑÑ html = null в каÑеÑÑве ÑезÑлÑÑаÑа.
Ðак Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ ÑÑо ÑделаÑÑ?
ÐÑевиднÑм ÑеÑением бÑло Ð±Ñ Ð¿ÑовеÑиÑÑ Ð·Ð½Ð°Ñение Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ if или ÑÑловного опеÑаÑоÑа ?, пÑежде Ñем обÑаÑаÑÑÑÑ Ðº его ÑвойÑÑвÑ, Ð²Ð¾Ñ Ñак:
let user = {};
alert(user.address ? user.address.street : undefined);
ÐÑо ÑабоÑаеÑ, ÑÑÑ Ð½ÐµÑ Ð¾Ñибки⦠Ðо ÑÑо доволÑно неÑлеганÑно. Ðак Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе видеÑÑ, "user.address" поÑвлÑеÑÑÑ Ð² коде дваждÑ.
ÐÐ¾Ñ ÐºÐ°Ðº Ñо же Ñамое вÑглÑдело Ð±Ñ Ð´Ð»Ñ document.querySelector:
let html = document.querySelector('.elem') ? document.querySelector('.elem').innerHTML : null;
Ðак видно, поиÑк ÑлеменÑа document.querySelector('.elem') здеÑÑ Ð²ÑзÑваеÑÑÑ Ð´Ð²Ð°Ð¶Ð´Ñ, ÑÑо не оÑÐµÐ½Ñ Ñ
оÑоÑо.
ÐÐ»Ñ Ð±Ð¾Ð»ÐµÐµ глÑбоко вложеннÑÑ ÑвойÑÑв ÑÑо еÑÑ Ð¼ÐµÐ½ÐµÐµ кÑаÑиво, поÑколÑÐºÑ Ð¿Ð¾ÑÑебÑеÑÑÑ Ð±Ð¾Ð»ÑÑе повÑоÑений.
РпÑимеÑÑ, давайÑе аналогиÑно вÑÑиÑлим user.address.street.name.
Ðам нÑжно пÑовеÑиÑÑ ÐºÐ°Ðº user.address, Ñак и user.address.street:
let user = {}; // Ñ Ð¿Ð¾Ð»ÑзоваÑÐµÐ»Ñ Ð½ÐµÑ Ð°Ð´ÑеÑа
alert(user.address ? user.address.street ? user.address.street.name : null : null);
ÐÑо пÑоÑÑо ÑжаÑно, Ñ ÐºÐ¾Ð³Ð¾-Ñо могÑÑ Ð´Ð°Ð¶Ðµ возникнÑÑÑ Ð¿ÑÐ¾Ð±Ð»ÐµÐ¼Ñ Ñ Ð¿Ð¾Ð½Ð¸Ð¼Ð°Ð½Ð¸ÐµÐ¼ Ñакого кода.
ÐÑÑÑ Ð½ÐµÐ¼Ð½Ð¾Ð³Ð¾ лÑÑÑий ÑпоÑоб напиÑаÑÑ ÑÑо, иÑполÑзÑÑ Ð¾Ð¿ÐµÑаÑÐ¾Ñ &&:
let user = {}; // полÑзоваÑÐµÐ»Ñ Ð±ÐµÐ· адÑеÑа
alert( user.address && user.address.street && user.address.street.name ); // undefined (без оÑибки)
ÐÑоÑ
од пÑи помоÑи логиÑеÑкого опеÑаÑоÑа Ð && ÑеÑез веÑÑ Ð¿ÑÑÑ Ðº ÑвойÑÑÐ²Ñ Ð³Ð°ÑанÑиÑÑеÑ, ÑÑо вÑе компоненÑÑ ÑÑÑеÑÑвÑÑÑ (еÑли неÑ, вÑÑиÑление пÑекÑаÑаеÑÑÑ), но Ñакже не ÑвлÑеÑÑÑ Ð¸Ð´ÐµÐ°Ð»ÑнÑм.
Ðак Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе видеÑÑ, имена ÑвойÑÑв по-пÑÐµÐ¶Ð½ÐµÐ¼Ñ Ð´ÑблиÑÑÑÑÑÑ Ð² коде. ÐапÑимеÑ, в пÑиведÑнном вÑÑе коде user.address поÑвлÑеÑÑÑ ÑÑи Ñаза.
ÐÐ¾Ñ Ð¿Ð¾ÑÐµÐ¼Ñ Ð² ÑзÑк бÑла добавлена опÑионалÑÐ½Ð°Ñ ÑепоÑка ?.. ЧÑÐ¾Ð±Ñ ÑеÑиÑÑ ÑÑÑ Ð¿ÑÐ¾Ð±Ð»ÐµÐ¼Ñ â Ñаз и навÑегда!
ÐпÑионалÑÐ½Ð°Ñ ÑепоÑка
ÐпÑионалÑÐ½Ð°Ñ ÑепоÑка ?. оÑÑÐ°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ Ð²ÑÑиÑление и возвÑаÑÐ°ÐµÑ undefined, еÑли знаÑение пеÑед ?. Ñавно undefined или null.
Ðалее в ÑÑой ÑÑаÑÑе, Ð´Ð»Ñ ÐºÑаÑкоÑÑи, Ð¼Ñ Ð±Ñдем говоÑиÑÑ, ÑÑо ÑÑо-Ñо «ÑÑÑеÑÑвÑеÑ», еÑли оно не ÑвлÑеÑÑÑ null и не undefined.
ÐÑÑгими Ñловами, value?.prop:
- ÑабоÑÐ°ÐµÑ ÐºÐ°Ðº
value.prop, еÑли знаÑениеvalueÑÑÑеÑÑвÑеÑ, - в пÑоÑивном ÑлÑÑае (когда
valueÑавноundefined/null) он возвÑаÑаеÑundefined.
ÐÐ¾Ñ Ð±ÐµÐ·Ð¾Ð¿Ð°ÑнÑй ÑпоÑоб полÑÑиÑÑ Ð´Ð¾ÑÑÑп к user.address.street, иÑполÑзÑÑ ?.:
let user = {}; // полÑзоваÑÐµÐ»Ñ Ð±ÐµÐ· адÑеÑа
alert( user?.address?.street ); // undefined (без оÑибки)
Ðод лакониÑнÑй и понÑÑнÑй, в нем вообÑе Ð½ÐµÑ Ð´ÑблиÑованиÑ.
Ð Ð²Ð¾Ñ Ð¿ÑÐ¸Ð¼ÐµÑ Ñ document.querySelector:
let html = document.querySelector('.elem')?.innerHTML; // бÑÐ´ÐµÑ undefined, еÑли ÑлеменÑа неÑ
СÑиÑÑвание адÑеÑа Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ user?.address ÑабоÑаеÑ, даже еÑли обÑÐµÐºÑ user не ÑÑÑеÑÑвÑеÑ:
let user = null;
alert( user?.address ); // undefined
alert( user?.address.street ); // undefined
ÐбÑаÑиÑе внимание: ÑинÑакÑÐ¸Ñ ?. Ð´ÐµÐ»Ð°ÐµÑ Ð½ÐµÐ¾Ð±ÑзаÑелÑнÑм знаÑение пеÑед ним, но не какое-либо поÑледÑÑÑее.
Так напÑимеÑ, в запиÑи user?.address.street.name ?. позволÑÐµÑ user безопаÑно бÑÑÑ null/undefined (и в ÑÑом ÑлÑÑае возвÑаÑÐ°ÐµÑ undefined), но ÑÑо Ñак ÑолÑко Ð´Ð»Ñ user. ÐоÑÑÑп к поÑледÑÑÑим ÑвойÑÑвам оÑÑÑеÑÑвлÑеÑÑÑ Ð¾Ð±ÑÑнÑм ÑпоÑобом. ÐÑли Ð¼Ñ Ñ
оÑим, ÑÑÐ¾Ð±Ñ Ð½ÐµÐºÐ¾ÑоÑÑе из ниÑ
бÑли необÑзаÑелÑнÑми, Ñогда нам нÑжно бÑÐ´ÐµÑ Ð·Ð°Ð¼ÐµÐ½Ð¸ÑÑ Ð±Ð¾Ð»ÑÑе . на ?..
Ðам ÑледÑÐµÑ Ð¸ÑполÑзоваÑÑ ?. ÑолÑко Ñам, где ноÑмалÑно, ÑÑо Ñего-Ñо не ÑÑÑеÑÑвÑеÑ.
РпÑимеÑÑ, еÑли, в ÑооÑвеÑÑÑвии Ñ Ð»Ð¾Ð³Ð¸ÐºÐ¾Ð¹ наÑего кода, обÑÐµÐºÑ user должен ÑÑÑеÑÑвоваÑÑ, но address ÑвлÑеÑÑÑ Ð½ÐµÐ¾Ð±ÑзаÑелÑнÑм, Ñо нам ÑледÑÐµÑ Ð¿Ð¸ÑаÑÑ user.address?.street, но не user?.address?.street.
Ð ÑÑом ÑлÑÑае, еÑли вдÑÑг user окажеÑÑÑ undefined, Ð¼Ñ Ñвидим пÑогÑаммнÑÑ Ð¾ÑÐ¸Ð±ÐºÑ Ð¿Ð¾ ÑÑÐ¾Ð¼Ñ Ð¿Ð¾Ð²Ð¾Ð´Ñ Ð¸ иÑпÑавим еÑ. РпÑоÑивном ÑлÑÑае, еÑли ÑлиÑком ÑаÑÑо иÑполÑзоваÑÑ ?., оÑибки могÑÑ Ð·Ð°Ð¼Ð°Ð»ÑиваÑÑÑÑ Ñам, где ÑÑо неÑмеÑÑно, и иÑ
бÑÐ´ÐµÑ Ñложнее оÑлаживаÑÑ.
?. должна бÑÑÑ Ð¾Ð±ÑÑвленаÐÑли пеÑеменной user вообÑе неÑ, Ñо user?.anything пÑиведÑÑ Ðº оÑибке:
// ReferenceError: user is not defined
user?.address;
ÐеÑÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð´Ð¾Ð»Ð¶Ð½Ð° бÑÑÑ Ð¾Ð±ÑÑвлена (к пÑимеÑÑ, как let/const/var user или как паÑамеÑÑ ÑÑнкÑии). ÐпÑионалÑÐ½Ð°Ñ ÑепоÑка ÑабоÑÐ°ÐµÑ ÑолÑко Ñ Ð¾Ð±ÑÑвленнÑми пеÑеменнÑми.
СокÑаÑÑнное вÑÑиÑление
Ðак бÑло Ñказано Ñанее, ?. немедленно оÑÑÐ°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ Ð²ÑÑиÑление, еÑли Ð»ÐµÐ²Ð°Ñ ÑаÑÑÑ Ð½Ðµ ÑÑÑеÑÑвÑеÑ.
Так ÑÑо еÑли поÑле ?. еÑÑÑ ÐºÐ°ÐºÐ¸Ðµ-Ñо вÑÐ·Ð¾Ð²Ñ ÑÑнкÑий или опеÑаÑии, Ñо они не пÑоизойдÑÑ.
ÐапÑимеÑ:
let user = null;
let x = 0;
user?.sayHi(x++); // Ð½ÐµÑ "user", поÑÑÐ¾Ð¼Ñ Ð²Ñполнение не доÑÑÐ¸Ð³Ð°ÐµÑ Ð²Ñзова sayHi и x++
alert(x); // 0, знаÑение не ÑвелиÑилоÑÑ
ÐÑÑгие ваÑианÑÑ Ð¿ÑименениÑ: ?.(), ?.[]
ÐпÑионалÑÐ½Ð°Ñ ÑепоÑка ?. â ÑÑо не опеÑаÑоÑ, а ÑпеÑиалÑÐ½Ð°Ñ ÑинÑакÑиÑеÑÐºÐ°Ñ ÐºÐ¾Ð½ÑÑÑÑкÑиÑ, коÑоÑÐ°Ñ Ñакже ÑабоÑÐ°ÐµÑ Ñ ÑÑнкÑиÑми и квадÑаÑнÑми Ñкобками.
ÐапÑимеÑ, ?.() иÑполÑзÑеÑÑÑ Ð´Ð»Ñ Ð²Ñзова ÑÑнкÑии, коÑоÑÐ°Ñ Ð¼Ð¾Ð¶ÐµÑ Ð½Ðµ ÑÑÑеÑÑвоваÑÑ.
РпÑиведÑнном ниже коде Ñ Ð½ÐµÐºÐ¾ÑоÑÑÑ
наÑиÑ
полÑзоваÑелей еÑÑÑ Ð¼ÐµÑод admin, а Ñ Ð½ÐµÐºÐ¾ÑоÑÑÑ
его неÑ:
let userAdmin = {
admin() {
alert("Я админ");
}
};
let userGuest = {};
userAdmin.admin?.(); // Я админ
userGuest.admin?.(); // ниÑего не пÑÐ¾Ð¸Ð·Ð¾Ð¹Ð´ÐµÑ (Ñакого меÑода неÑ)
ÐдеÑÑ Ð² обеиÑ
ÑÑÑокаÑ
Ð¼Ñ ÑнаÑала иÑполÑзÑем ÑоÑÐºÑ (userAdmin.admin), ÑÑÐ¾Ð±Ñ Ð¿Ð¾Ð»ÑÑиÑÑ ÑвойÑÑво admin, поÑÐ¾Ð¼Ñ ÑÑо Ð¼Ñ Ð¿Ñедполагаем, ÑÑо обÑÐµÐºÑ userAdmin ÑÑÑеÑÑвÑеÑ, Ñак ÑÑо ÑиÑаÑÑ Ð¸Ð· него безопаÑно.
ÐаÑем ?.() пÑовеÑÑÐµÑ Ð»ÐµÐ²ÑÑ ÑаÑÑÑ: еÑли ÑÑнкÑÐ¸Ñ admin ÑÑÑеÑÑвÑеÑ, Ñо она запÑÑкаеÑÑÑ (ÑÑо Ñак Ð´Ð»Ñ userAdmin). РпÑоÑивном ÑлÑÑае (Ð´Ð»Ñ userGuest) вÑÑиÑление оÑÑановиÑÑÑ Ð±ÐµÐ· оÑибок.
СинÑакÑÐ¸Ñ ?.[] Ñакже ÑабоÑаеÑ, еÑли Ð¼Ñ Ñ
оÑим иÑполÑзоваÑÑ Ñкобки [] Ð´Ð»Ñ Ð´Ð¾ÑÑÑпа к ÑвойÑÑвам вмеÑÑо ÑоÑки .. Ðак и в пÑедÑдÑÑиÑ
ÑлÑÑаÑÑ
, он позволÑÐµÑ Ð±ÐµÐ·Ð¾Ð¿Ð°Ñно ÑÑиÑÑваÑÑ ÑвойÑÑво из обÑекÑа, коÑоÑÑй Ð¼Ð¾Ð¶ÐµÑ Ð½Ðµ ÑÑÑеÑÑвоваÑÑ.
let key = "firstName";
let user1 = {
firstName: "John"
};
let user2 = null;
alert( user1?.[key] ); // John
alert( user2?.[key] ); // undefined
Также Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ иÑполÑзоваÑÑ ?. Ñ delete:
delete user?.name; // ÑдалÑÐµÑ user.name еÑли полÑзоваÑÐµÐ»Ñ ÑÑÑеÑÑвÑеÑ
?. Ð´Ð»Ñ Ð±ÐµÐ·Ð¾Ð¿Ð°Ñного ÑÑÐµÐ½Ð¸Ñ Ð¸ ÑдалениÑ, но не Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸ÑиÐпÑионалÑÐ½Ð°Ñ ÑепоÑка ?. не Ð¸Ð¼ÐµÐµÑ ÑмÑÑла в левой ÑаÑÑи пÑиÑваиваниÑ.
ÐапÑимеÑ:
let user = null;
user?.name = "John"; // ÐÑибка, не ÑабоÑаеÑ
// Ñо же Ñамое ÑÑо напиÑаÑÑ undefined = "John"
ÐÑого
СинÑакÑÐ¸Ñ Ð¾Ð¿ÑионалÑной ÑепоÑки ?. Ð¸Ð¼ÐµÐµÑ ÑÑи ÑоÑмÑ:
obj?.propâ возвÑаÑаеÑobj.propеÑлиobjÑÑÑеÑÑвÑеÑ, в пÑоÑивном ÑлÑÑаеundefined.obj?.[prop]â возвÑаÑаеÑobj[prop]еÑлиobjÑÑÑеÑÑвÑеÑ, в пÑоÑивном ÑлÑÑаеundefined.obj.method?.()â вÑзÑваеÑobj.method(), еÑлиobj.methodÑÑÑеÑÑвÑеÑ, в пÑоÑивном ÑлÑÑае возвÑаÑаеÑundefined.
Ðак Ð¼Ñ Ð²Ð¸Ð´Ð¸Ð¼, вÑе они пÑоÑÑÑ Ð¸ понÑÑÐ½Ñ Ð² иÑполÑзовании. ?. пÑовеÑÑÐµÑ Ð»ÐµÐ²ÑÑ ÑаÑÑÑ Ð½Ð° null/undefined и позволÑÐµÑ Ð¿ÑодолжиÑÑ Ð²ÑÑиÑление, еÑли ÑÑо не Ñак.
ЦепоÑка ?. позволÑÐµÑ Ð±ÐµÐ·Ð¾Ð¿Ð°Ñно полÑÑаÑÑ Ð´Ð¾ÑÑÑп к вложеннÑм ÑвойÑÑвам.
Тем не менее, Ð¼Ñ Ð´Ð¾Ð»Ð¶Ð½Ñ Ð¸ÑполÑзоваÑÑ ?. оÑÑоÑожно, ÑолÑко Ñам, где по логике кода допÑÑÑимо, ÑÑо Ð»ÐµÐ²Ð°Ñ ÑаÑÑÑ Ð½Ðµ ÑÑÑеÑÑвÑеÑ. ЧÑÐ¾Ð±Ñ Ð¾Ð½ не ÑкÑÑвал Ð¾Ñ Ð½Ð°Ñ Ð¾Ñибки пÑогÑаммиÑованиÑ, еÑли они возникнÑÑ.
ÐомменÑаÑии
<code>, Ð´Ð»Ñ Ð½ÐµÑколÑÐºÐ¸Ñ ÑÑÑок кода — Ñег<pre>, еÑли болÑÑе 10 ÑÑÑок — ÑÑÑÐ»ÐºÑ Ð½Ð° пеÑоÑниÑÑ (plnkr, JSBin, codepenâ¦)