The optional chaining ?. is a safe way to access nested object properties, even if an intermediate property doesnât exist.
The ânon-existing propertyâ problem
إذا ÙÙØª ÙØ¯ بدأت ÙÙØªÙ ÙÙ ÙØ±Ø§Ø¡Ø© ÙØ°Ù Ø§ÙØ¨Ø±Ùا٠ج Ø§ÙØªØ¹ÙÙÙ Ù Ø§ÙØ®Ø§ØµÙ بÙÙ JavaScriptØ ÙØ±Ø¨Ù ا ÙÙ ØªÙØ§Ø¬Ù ÙØ°Ù اÙÙ Ø´ÙÙØ© Ù Ù ÙØ¨Ù ÙÙÙÙÙØ§ شائعة جداÙ.
As an example, letâs say we have user objects that hold the information about our users.
Most of our users have addresses in user.address property, with the street user.address.street, but some did not provide them.
In such case, when we attempt to get user.address.street, and the user happens to be without an address, we get an error:
let user = {}; // a user without "address" property
alert(user.address.street); // ÙØ¨Ø§ÙتاÙÙ ÙØØ¯Ø« Ø§ÙØ®Ø·Ø£ Ø¹ÙØ¯ Ù
ØØ§ÙÙØ© اÙÙØµÙÙ ÙÙØ®Ùاص Ø£Ù Ø§ÙØÙÙ٠ضÙ
ÙÙ
Thatâs the expected result. JavaScript works like this. As user.address is undefined, an attempt to get user.address.street fails with an error.
In many practical cases weâd prefer to get undefined instead of an error here (meaning âno streetâ).
â¦And another example. In the web development, we can get an object that corresponds to a web page element using a special method call, such as document.querySelector('.elem'), and it returns null when thereâs no such element.
// document.querySelector('.elem') is null if there's no element
let html = document.querySelector('.elem').innerHTML; // error if it's null
Once again, if the element doesnât exist, weâll get an error accessing .innerHTML of null. And in some cases, when the absence of the element is normal, weâd like to avoid the error and just accept html = null as the result.
How can we do this?
The obvious solution would be to check the value using if or the conditional operator ?, before accessing its property, like this:
let user = {};
alert(user.address ? user.address.street : undefined);
It works, thereâs no error⦠But itâs quite inelegant. As you can see, the "user.address" appears twice in the code. For more deeply nested properties, that becomes a problem as more repetitions are required.
E.g. letâs try getting user.address.street.name.
We need to check both user.address and user.address.street:
let user = {}; // user has no address
alert(user.address ? user.address.street ? user.address.street.name : null : null);
Thatâs just awful, one may even have problems understanding such code.
Donât even care to, as thereâs a better way to write it, using the && operator:
let user = {}; // غرض ÙÙ
ستخدÙ
ÙØ§ ÙÙ
Ù٠عÙÙØ§Ù
alert( user.address && user.address.street && user.address.street.name ); // undefined (no error)
ANDâing the whole path to the property ensures that all components exist (if not, the evaluation stops), but also isnât ideal.
As you can see, property names are still duplicated in the code. E.g. in the code above, user.address appears three times.
Thatâs why the optional chaining ?. was added to the language. To solve this problem once and for all!
Ø§ÙØªØ³ÙØ³Ù Ø§ÙØ§Ø®ØªÙار٠(ØºÙØ± Ø§ÙØ¥Ø¬Ø¨Ø§Ø±Ù)
The optional chaining ?. stops the evaluation if the value before ?. is undefined or null and returns undefined.
ÙÙÙØ¥ÙØ¬Ø§Ø²Ø Ø³ÙÙÙ٠ضÙ
Ù ÙØ°Ù اÙÙ
ÙØ§ÙØ© Ø£Ù Ø´ÙØ¦Ø§Ù Ù
ا âÙ
ÙØ¬ÙØ¯â Ø¥Ø°Ø§ ÙÙ
تÙÙ ÙÙÙ
ت٠null ÙÙÙ
تÙÙ undefined ÙØ°ÙÙ.
In other words, value?.prop:
- works as
value.prop, ifvalueexists, - otherwise (when
valueisundefined/null) it returnsundefined.
Hereâs the safe way to access user.address.street using ?.:
let user = {}; // غرض اÙÙ
ستخدÙ
Ø§ÙØªØ§ÙÙ ÙØ§ ÙÙ
ÙÙ Ø®Ø§ØµÙØ© Ø§ÙØ¹ÙÙØ§Ù
alert( user?.address?.street ); // Ø³ÙØ¸Ùر ÙÙØ§ بدÙÙ ØØ¯ÙØ« خطأ undefined
The code is short and clean, thereâs no duplication at all.
Reading the address with user?.address works even if user object doesnât exist:
let user = null;
alert( user?.address ); // undefined
alert( user?.address.street ); // undefined
ÙØ±Ø¬Ù Ù
ÙØ§ØØ¸Ø© Ø£Ù Ø§ÙØªØ±ÙÙØ¨ Ø£Ù Ø§ÙØ´ÙÙ .? ÙØ¹Ù
٠بشÙ٠صØÙØ ØÙØ« ÙØªÙ
ÙØ¶Ø¹Ù Ø¨Ø§ÙØ¶Ø¨Ø·Ø ÙÙÙØ³ بعد ذÙ٠اÙÙ
ÙØ§Ù Ø§ÙØ°Ù تÙ
ÙØ¶Ø¹Ù ÙÙÙ.
E.g. in user?.address.street.name the ?. allows user to safely be null/undefined (and returns undefined in that case), but thatâs only for user. Further properties are accessed in a regular way. If we want some of them to be optional, then weâll need to replace more . with ?..
ÙÙÙ٠إذا ÙØ§Ù Ø§ÙØºØ±Ø¶ user Ù
ÙØ¬Ùدا٠باÙÙØ¹ÙØ ÙÙØ¬Ø¨ أ٠تÙÙÙ Ø§ÙØ®ØµØ§Ø¦Øµ اÙÙØ³Ùطة Ù
ÙØ¬Ùدة ÙÙÙØµØ¯ Ø¨Ø§ÙØ®ØµØ§Ø¦Øµ اÙÙØ³Ùطة user.address Ù
Ø«ÙØ§Ù.
For example, if according to our coding logic user object must exist, but address is optional, then we should write user.address?.street, but not user?.address?.street.
So, if user happens to be undefined due to a mistake, weâll see a programming error about it and fix it. Otherwise, coding errors can be silenced where not appropriate, and become more difficult to debug.
````warn header="اÙÙ
تØÙ٠اÙÙØ§Ùع ÙØ¨Ù Ø§ÙØªØ±ÙÙØ¨ `.?` ÙØ¬Ø¨ Ø£Ù ÙÙÙÙ Ù
عرÙÙØ§Ù"
إذا ÙÙ
ÙØªÙ
٠تعرÙ٠اÙÙ
تØÙÙ `user`Ø Ø³ÙØ¤Ø¯Ù Ø§ÙØªØ¹Ø¨Ùر `user?.anything` Ø¥ÙÙ ØØµÙ٠خطأ:
```js run
// ReferenceError: user is not defined
user?.address;
The variable must be declared (e.g. let/const/var user or as a function parameter). The optional chaining works only for declared variables.
## اختصار Ø§ÙØ·Ø±Ù (Short-circuiting)
ÙÙ
ا تÙ
Ù Ø°ÙØ±Ù Ø¢ÙÙØ§ÙØ ÙÙÙÙ
Ø§ÙØªØ±ÙÙØ¨ `.?` بإÙÙØ§Ù عÙ
ÙÙØ© تÙÙÙÙ
اÙÙÙØ¯ Ø§ÙØ¨Ø±Ù
ج٠(ÙØ®ØªØµØ± Ø§ÙØ·Ø±ÙÙ) إذا ÙÙ
ÙÙ٠اÙÙØ³Ù
اÙÙØ³Ø§Ø±Ù (عÙÙ ÙØ³Ø§Ø± Ø§ÙØªØ±ÙÙØ¨) Ù
ÙØ¬ÙداÙ.
So, if there are any further function calls or side effects, they don't occur.
For instance:
```js run
let user = null;
let x = 0;
user?.sayHi(x++); // no "sayHi", so the execution doesn't reach x++
alert(x); // ÙØ§ ÙØªÙ
Ø²ÙØ§Ø¯Ø© اÙÙÙÙ
Ø© 0
```
## Other variants: ?.(), ?.[]
ÙØ§ ÙÙØªØµØ± Ø§ÙØªØ³ÙØ³Ù Ø§ÙØ§Ø®ØªÙار٠`.?` Ù٠عÙ
Ù٠عÙ٠اÙÙ
تØÙÙØ§Øª ÙÙØ· ÙÙÙ ÙÙØ³ بعاÙ
Ù (Ø±ÙØ§Ø¶Ù) ÙØ§ÙجÙ
ع ÙØ§ÙØ·Ø±ØØ ب٠Ù٠ترÙÙØ¨ بÙÙÙÙ ÙØ¹Ù
Ù Ø£ÙØ¶Ø§Ù عÙÙ Ø§ÙØªÙابع ÙØ§ÙØ£ÙÙØ§Ø³ اÙÙ
ربعة (Ø£ÙÙØ§Ø³ اÙÙ
صÙÙÙØ§Øª).
عÙ٠سبÙ٠اÙÙ
Ø«Ø§ÙØ ÙÙ
Ù٠استخداÙ
Ø§ÙØªØ±ÙÙØ¨ `().?` ÙØ§Ø³ØªØ¯Ø¹Ø§Ø¡ تابع ÙØ¯ ÙØ§ ÙÙÙÙ Ù
عرÙÙØ§Ù Ø¨Ø§ÙØ£ØµÙ.
Ù٠اÙÙ
Ø«Ø§Ù Ø£Ø¯ÙØ§ÙØ ÙÙ
تÙ٠بعض أغراض اÙÙ
ستخدÙ
ÙÙ Ø§ÙØ·Ø±ÙÙØ© (method) Ø£Ù Ø§ÙØªØ§Ø¨Ø¹ اÙÙ
ÙØ³Ù
Ù `admin` ÙØ¨Ø¹Ø¶ÙÙ
Ø§ÙØ¢Ø®Ø± ÙØ§ ÙÙ
تÙÙ:
```js run
let userAdmin = {
admin() {
alert("I am admin");
}
};
let userGuest = {};
userAdmin.admin?.(); // I am admin
userGuest.admin?.(); // nothing (no such method)
```
Here, in both lines we first use the dot (`userAdmin.admin`) to get `admin` property, because we assume that the user object exists, so it's safe read from it.
Then `?.()` checks the left part: if the admin function exists, then it runs (that's so for `userAdmin`). Otherwise (for `userGuest`) the evaluation stops without errors.
ÙÙ ØØ§Ù Ø§ÙØ±ØºØ¨Ø© باستخداÙ
Ø§ÙØ£ÙÙØ§Ø³ اÙÙ
Ø±Ø¨ÙØ¹Ø© `[]` Ø¨Ø¯ÙØ§Ù Ù
٠اÙÙÙØ·Ø© `.` ÙÙÙØµÙÙ ÙÙØ®Ùاص ضÙ
٠غرض Ø£Ù ÙØ§Ø¦Ù Ù
Ø§Ø Ø³ÙÙÙ Ø§ÙØªØ¹Ø¨Ùر `[].?` Ø¨Ø§ÙØºØ±Ø¶ Ø£ÙØ¶Ø§Ù. ÙØ¨Ø´ÙÙ Ù
شاب٠ÙÙØØ§ÙØ§Øª Ø§ÙØ³Ø§Ø¨ÙØ©Ø ÙØ³Ù
Ø ÙØ°Ø§ Ø§ÙØªØ¹Ø¨Ùر بشÙ٠آÙ
Ù ÙØ±Ø§Ø¡Ø©Ù Ø®Ø§ØµÙØ© Ø£Ù ØÙ٠ضÙ
٠غرض Ù
عÙÙÙ ÙØ¯ ÙØ§ ÙÙÙÙ Ù
ÙØ¬ÙداÙ.
```js run
let key = "firstName";
let user1 = {
firstName: "John"
};
let user2 = null;
alert( user1?.[key] ); // John
alert( user2?.[key] ); // undefined
```
ÙØ°ÙÙ ÙÙ
ÙÙÙØ§ استخداÙ
Ø§ÙØªØ±ÙÙØ¨ `.?` Ù
ع Ø§ÙØªØ¹Ø¨Ùر `delete`:
```js run
delete user?.name; // سÙÙÙÙ
Ø¨ØØ°Ù اسÙ
اÙÙ
ستخدÙ
ÙÙ ØØ§Ù ÙØ§Ù غرض اÙÙ
ستخدÙ
Ù
ÙØ¬ÙداÙ
```
````warn header="We can use `?.` for safe reading and deleting, but not writing"
The optional chaining `?.` has no use at the left side of an assignment.
For example:
```js run
let user = null;
user?.name = "John"; // ÙØ³ÙØØ¯Ø« Ø®Ø·Ø£Ø ÙØ£Ù ÙØ°Ù Ø§ÙØ·Ø±ÙÙØ© ÙØ§ تعÙ
Ù
// ÙØ£ÙÙ Ø³ÙØªÙ
تÙÙÙÙ
ÙØ§ عÙ٠أÙ
// undefined = "John"
```
It's just not that smart.
Summary
The optional chaining ?. syntax has three forms:
obj?.propâ returnsobj.propifobjexists, otherwiseundefined.obj?.[prop]â returnsobj[prop]ifobjexists, otherwiseundefined.obj.method?.()â callsobj.method()ifobj.methodexists, otherwise returnsundefined.
ÙÙÙ
ا ÙØ±ÙØ Ø¬Ù
ÙØ¹ Ø§ÙØ·Ø±Ù Ø§ÙØ³Ø§Ø¨ÙØ© ÙØ§Ø¶ØØ© ÙØ³ÙÙØ© Ø§ÙØ§Ø³ØªØ®Ø¯Ø§Ù
. ÙØ§ÙترÙÙØ¨ .? ÙØªØÙÙ Ù
Ù Ø§ÙØ¬Ø²Ø¡ Ø§ÙØ£Ùسر ÙÙÙ
ا إذا ÙÙ
ÙÙÙ null/undefined ÙÙØ³Ù
Ø Ø¨Ø¹Ø¯ÙØ§ بإÙÙ
ا٠عÙ
ÙÙØ© Ø§ÙØªÙÙÙÙ
.
ÙØ¥Ø°Ø§ ÙØ§Ù ÙØ¯ÙÙØ§ خصائص Ù
ØªØ¯Ø§Ø®ÙØ© ÙÙÙ
ا بÙÙÙØ§Ø ÙÙØ³Ù
Ø ØªØ³ÙØ³Ù Ù
Ù Ø§ÙØªØ±ÙÙØ¨ .? Ø¨ÙØ±Ø§Ø¦ØªÙا بشÙÙ٠آÙ
Ù.
Still, we should apply ?. carefully, only where itâs acceptable that the left part doesnât exist. So that it wonât hide programming errors from us, if they occur.
Ø§ÙØªØ¹ÙÙÙØ§Øª
<code>Ø ÙÙÙÙØ«Ùر Ù Ù Ø§ÙØ³Ø·Ùر استخدÙ<pre>Ø ÙÙØ£Ùثر Ù Ù 10 Ø³Ø·ÙØ± استخد٠(plnkr, JSBin, codepenâ¦)