Skip to content

Optional chaining '?.' #70

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 18, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 79 additions & 55 deletions 1-js/04-object-basics/07-optional-chaining/article.md
Original file line number Diff line number Diff line change
@@ -1,60 +1,69 @@
# Optional chaining '?.' یا زنجیره ای اختیاری
[recent browser="new"]

# Optional chaining '?.'
###### زنجیره ای اختیاری روشی بدون خطا برای دستیابی به ویژگی های(properties) داخلی شی است حتی در زمانی که ویژگی میانی وجود نداشته باشد

[recent browser="new"]

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
اگر به تازگی شروع به خواندن آموزش و یادگیری جاوا اسکریپت کرده اید ، شاید این مشکل هنوز شما را لمس نکرده اید ، اما این یک مشکل کاملاً رایج است.

If you've just started to read the tutorial and learn JavaScript, maybe the problem hasn't touched you yet, but it's quite common.
برای مثال, بیاید یک شی(object) برای اطلاعات کاربران در نظر بگیریم. تعدادی از کاربران ما آدرس را در ویژگی `user.address` و خیابان را در ویژگی `user.address.street` دارند ولی تعدادی کمی از آن ها آدرس را ارائه نکرده‌اند.

As an example, let's consider objects for user data. Most of our users have addresses in `user.address` property, with the street `user.address.street`, but some did not provide them.
در این صورت تلاش ما برای دستیابی به `user.address.street` با شکست مواجه خواهد شد

In such case, when we attempt to get `user.address.street`, we'll get an error:

```js run
let user = {}; // the user without "address" property

alert(user.address.street); // Error!
```

That's the expected result, JavaScript works like this, but many practical cases we'd prefer to get `undefined` instead of an error (meaning "no street").
این یک خروجی قابل حدس است٬ جاوااسکریپت اینگونه کار میکند٬ ولی در مثال های عملی ترجیح میدهیم ‍``undefined`` دریافت کنیم به جای خطا.

یا مثالی دیگر در توسعه وب٬ ما میخواهیم اطلاعاتی در مورد اِلمانی در صفحه را بگیریم٬ که ممکن بعضی اوقات وجود نداشته باشد :


...And another example. In the web development, we may need to get an information about an element on the page, that sometimes doesn't exist:

یا در توسعه وب٬ ما میخواهیم اطلاعاتی در مورد اِلمان در صفحه را بگیریم٬ ولی شاید وجود نداشته باشد :

```js run
// Error if the result of querySelector(...) is null
let html = document.querySelector('.my-element').innerHTML;
```

Before `?.` appeared in the language, the `&&` operator was used to work around that.

For example:

قبل از اینکه `?.` در زبان وجود داشته باشد از عمگر `&&` برای کار در این مورد استفاده میشد. برای مثال :

```js run
let user = {}; // user has no address

alert( user && user.address && user.address.street ); // undefined (no error)
```

AND'ing the whole path to the property ensures that all components exist (if not, the evaluation stops), but is cumbersome to write.
AND کردن کل مسیر رسیدن به ویژگی ، وجود همه اجزا را تضمین می کند(اگر ارزیابی متوقف نشود) ، اما نوشتن آن دست و پا گیر است.


## زنجیره ای اختیاری

## Optional chaining
زنجیره ای اختیاری `?.` ارزیابی را متوقف میکند اگر مقدار قبل از قسمت `?.` برابر با `undefined` یا `null` باشد و مقدار `undefined` را برمیگرداند.

The optional chaining `?.` stops the evaluation and returns `undefined` if the part before `?.` is `undefined` or `null`.
**در ادامه این مقاله ، به اختصار خواهیم گفت چیزی وجود خواهد داشت اگر که `undefined` و `null` نباشد.**

**Further in this article, for brevity, we'll be saying that something "exists" if it's not `null` and not `undefined`.**

Here's the safe way to access `user.address.street`:

این یک مسیر امن برای دستیابی `user.address.street` است :

```js run
let user = {}; // user has no address

alert( user?.address?.street ); // undefined (no error)
```

Reading the address with `user?.address` works even if `user` object doesn't exist:


خواندن آدرس با `user?.address` کار خواهد کرد حتی زمانی هم که شی `user` وجود ندارد :

```js run
let user = null;
Expand All @@ -63,39 +72,45 @@ alert( user?.address ); // undefined
alert( user?.address.street ); // undefined
```

Please note: the `?.` syntax makes optional the value before it, but not any further.
لطفا توجه داشته باشید : سینتکس `?.` مقدارهای قبلی را اختیاری میکند نه مقدارهای جلوی آن را.

در مثال بالا `user?.` به `user` مقدار `null/undefined` خواهد داد.

In the example above, `user?.` allows only `user` to be `null/undefined`.

On the other hand, if `user` does exist, then it must have `user.address` property, otherwise `user?.address.street` gives an error at the second dot.

```warn header="Don't overuse the optional chaining"
We should use `?.` only where it's ok that something doesn't exist.
از طرف دیگر ، اگر ‍‍`user` وجود داشته باشد ، پس باید ویژگی `user.address` داشته باشد ، در غیر این صورت `user؟.address.street `در نقطه دوم خطا می دهد.

For example, if according to our coding logic `user` object must be there, but `address` is optional, then `user.address?.street` would be better.
```warn header="از زنجیر اختیاری بیش از حد استفاده تکنید"

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.
ما باید از `?.` فقط زمانی استفاده کنیم که عدم وجود چیزی اشکالی ندارد

به عنوان مثال ، اگر مطابق منطق برنامه نویسی ما ، شی `user` باید وجود داشته باشد ولی `address` اختیاری است در آن شرایط استفاده از `user.address?.street` راه حل بهتری است ،

بنابراین ، اگر تصادفاً به دلیل اشتباهی ‍`user` برابر با `undefined` باشد، شاهد یک خطای برنامه نویسی در مورد آن خواهیم بود و آن را برطرف خواهیم کرد. در غیر این صورت ، خطاهای کد را می توان در مواردی که مناسب نیست ساکت کرد٬ و کار اشکال زدایی را سخت تر میکند.
```

````warn header="The variable before `?.` must be declared"
If there's no variable `user` at all, then `user?.anything` triggers an error:


````warn header="متغیر قبل از ؟. باید تعریف شده باشد" اگر متغیر user به هیچ وجه وجود نداشته باشد `user?.anything` خطا خواهد داد



```js run
// ReferenceError: user is not defined
user?.address;
```
There must be a declaration (e.g. `let/const/var user`). The optional chaining works only for declared variables.
````

## Short-circuiting
باید تعریفی باشد( `let/const/var user ` ). زنجیره ای اختیاری فقط برای متغیرهای تعریف شده کار می کند.
باید `let/const/var user ` وجود داشته باشد. زنجیره ای اختیاری فقط برای متغیرهای تعریف شده کار می کند.

As it was said before, the `?.` immediately stops ("short-circuits") the evaluation if the left part doesn't exist.
````

So, if there are any further function calls or side effects, they don't occur.
## اتصال کوتاه
همانطور که قبلا گفته شد عبارت `?.` فوراً ارزیابی را متوقف میکند(اتصال کوتاه) اگر عبارت سمت چپ آن وجود نداشته باشد.
بنابراین ، اگر صدا زدن تابعی یا عوارض جانبی دیگری وجود داشته باشد ، اتفاق نمی‌افتد.

For instance:
برای نمونه :

```js run
```js run
let user = null;
let x = 0;

Expand All @@ -104,13 +119,15 @@ user?.sayHi(x++); // no "sayHi", so the execution doesn't reach x++
alert(x); // 0, value not incremented
```

## Other variants: ?.(), ?.[]

The optional chaining `?.` is not an operator, but a special syntax construct, that also works with functions and square brackets.

For example, `?.()` is used to call a function that may not exist.
## ?.(), ?.[] : و موارد دیگر

زنجیره اختیاری `?.` یک عمگر نیست بلکه یک ساختار سینتکسی خاص است که با توابع و براکت ها نیز کار می کند

برای مثال `?.()` برای صدا زدن تابعی که ممکن است وجود نداشته باشد هم کاربرد دارد

In the code below, some of our users have `admin` method, and some don't:
در کد زیر٬ برخی از کاربران ما متد `admin` را دارند و برخی خیر :

```js run
let user1 = {
Expand All @@ -127,11 +144,11 @@ user2.admin?.();
*/!*
```

Here, in both lines we first use the dot (`user1.admin`) to get `admin` property, because the user object must exist, so it's safe read from it.
در اینجا در هر دو خط ما ابتدا از `.` (`user1.admin`) برای گرفتن ویژگی ‍`admin` استفاده میکنیم به خاطر اینکه شی ‍`user` حتما وجود دارد پس برای خواندن از آن مطمئن هستیم.

Then `?.()` checks the left part: if the admin function exists, then it runs (that's so for `user1`). Otherwise (for `user2`) the evaluation stops without errors.
سپس `?.()` عبارت سمت چپ را بررسی میکند: اگر تابع ‍`admin` وجود داشته باشد آنرا اجرا میکند(برای ‍`user1`) در غیر اینصورت(برای `user2`) محاسبات بدون خطا به متوقف میشود.

The `?.[]` syntax also works, if we'd like to use brackets `[]` to access properties instead of dot `.`. Similar to previous cases, it allows to safely read a property from an object that may not exist.
سینتکس برای حالت `?.[]` نیز کار میکند٬ اگر ما میخواهیم از براکت به جای نقطه برای دستیابی به ویژگی‌ها استفاده کنیم مشابه موارد قبلی ، اجازه می دهد تا با خیال راحت یک ویژگی را از یک شی که ممکن است وجود نداشته باشد، را بخوانیم.

```js run
let user1 = {
Expand All @@ -148,36 +165,43 @@ alert( user2?.[key] ); // undefined
alert( user1?.[key]?.something?.not?.existing); // undefined
```

Also we can use `?.` with `delete`:


همچنان ما میتوانیم از `?.` در `delete` هم استفاده کنیم

```js run
delete user?.name; // delete user.name if user exists
```

````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.
````warn header="ما میتوانیم از`؟.` برای پاک کردن و خواندن مطمئن استفاده کنیم ولی نوشتن نه."
زنجیره اختیاری `?.` هیچ کاربردی برای سمت چپ مساوی ندارد.

For example:

برای مثال:
```js run
let user = null;

user?.name = "John"; // Error, doesn't work
// because it evaluates to undefined = "John"
```

It's just not that smart.
````
آنقدرها هم هوشمند نیست.



## خلاصه

سینتکس `?.` سه شکل دارد:

## Summary
1. `obj?.prop` - مقدار ‍‍`obj.prop` را برمیگرداند اگر `obj` وجود داشته باشد در غیر اینصورت مقدار `undefined` را برمیگرداند
2. `[obj?.[prop` - مقدار ‍‍`[obj.[prop` را برمیگرداند اگر `obj` وجود داشته باشد در غیر اینصورت مقدار `undefined` را برمیگرداند
3. `()obj.method()` - ‍‍``obj?.method`` را صدا میزند اگر `obj` وجود داشته باشد در غیر اینصورت مقدار `undefined` را برمیگرداند

The optional chaining `?.` syntax has three forms:

1. `obj?.prop` -- returns `obj.prop` if `obj` exists, otherwise `undefined`.
2. `obj?.[prop]` -- returns `obj[prop]` if `obj` exists, otherwise `undefined`.
3. `obj.method?.()` -- calls `obj.method()` if `obj.method` exists, otherwise returns `undefined`.
همانطور که می بینیم ، همه آنها ساده و آسان برای استفاده هستند. `?.` سمت چپ را از نظر `null/undefined` بررسی می کند و اجازه می دهد تا ارزیابی ادامه یابد اگر برابر با `null/undefined` نباشد.

As we can see, all of them are straightforward and simple to use. The `?.` checks the left part for `null/undefined` and allows the evaluation to proceed if it's not so.
زنجیر `?.` امکان دسترسی به خواص تودرتو را فراهم میکند.

A chain of `?.` allows to safely access nested properties.
با این حال هنوز ما باید `?.` را با دقت اعمال کنیم ، فقط درصورتی قابل قبو است که سمت چپ ممکن است وجود نداشته باشد.

Still, we should apply `?.` carefully, only where it's acceptable that the left part doesn't to exist. So that it won't hide programming errors from us, if they occur.
با این حال خطاهای برنامه نویسی را از ما مخفی نمیکند اگر آنها اتفاق بیافتند.