Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Optional chaining '?.'#163

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
EpicHigh merged 8 commits intomasterfromoptional-chaining
Apr 15, 2024
Merged
Changes fromall commits
Commits
Show all changes
8 commits
Select commitHold shift + click to select a range
4f0b68d
Translate the "Objects: the basics" section to Thai and update constr…
EpicHighApr 15, 2024
82d9cfd
Translate the "Objects: the basics" section to Thai and update option…
EpicHighApr 15, 2024
f76c198
Update optional chaining examples and translate the "Objects: the bas…
EpicHighApr 15, 2024
372b755
Update optional chaining examples and translate the "Objects: the bas…
EpicHighApr 15, 2024
f27c3e7
Update optional chaining examples and translate the "Objects: the bas…
EpicHighApr 15, 2024
962af02
Update optional chaining examples and translate the "Objects: the bas…
EpicHighApr 15, 2024
99c76d1
Update optional chaining examples and translate the "Objects: the bas…
EpicHighApr 15, 2024
64b6699
Update optional chaining examples and translate the "Objects: the bas…
EpicHighApr 15, 2024
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
171 changes: 80 additions & 91 deletions1-js/04-object-basics/07-optional-chaining/article.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,97 @@

# Optional chaining '?.'
# Optional Chaining '?.'

[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
Optional chaining หรือ `?.` เป็นวิธีที่ปลอดภัยในการเข้าถึงคุณสมบัติแบบซ้อนของออบเจ็กต์ แม้ว่าคุณสมบัติระหว่างทางจะไม่มีอยู่ก็ตาม

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.
## ปัญหาเมื่อเข้าถึงคุณสมบัติที่ไม่มีอยู่

As an example, let's say we have `user` objects that hold the information about our users.
ยกตัวอย่างเช่น สมมติเรามีออบเจ็กต์ `user` ที่เก็บข้อมูลผู้ใช้ โดยผู้ใช้ส่วนใหญ่มีที่อยู่อยู่ใน `user.address` ซึ่งภายในมี `user.address.street` เก็บชื่อถนน แต่ผู้ใช้บางคนอาจไม่ได้ระบุที่อยู่ไว้

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:
เมื่อเราพยายามเข้าถึง `user.address.street` โดยที่ผู้ใช้ไม่มีที่อยู่ จะเกิดข้อผิดพลาดแบบนี้:

```js run
let user = {}; //a user without"address" property
let user = {}; //ผู้ใช้ที่ไม่มีคุณสมบัติ"address"

alert(user.address.street); // Error!
alert(user.address.street); //เกิดError!
```

That's the expected result.JavaScriptworks like this. As`user.address`is `undefined`, an attempt to get`user.address.street`fails with an error.
นั่นคือผลลัพธ์ปกติของJavaScriptเพราะ`user.address`เป็น `undefined` การเข้าถึง`user.address.street`จึงล้มเหลว

In many practical cases we'd prefer to get`undefined`instead of an error here (meaning "no street").
ในความเป็นจริง หลายครั้งเราอาจต้องการให้คืนค่า`undefined`กลับมาแทนข้อผิดพลาด (เช่น หมายถึง "ไม่มีถนน")

...and another example. In 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')` เพื่อหาองค์ประกอบใน HTML ถ้าไม่เจอจะคืนค่า `null`กลับมา แล้วเมื่อเราพยายามเข้าถึง property ของมัน อย่าง `.innerHTML` ก็จะเกิด error เช่นกัน:

```js run
// document.querySelector('.elem')is nullif there's noelement
let html = document.querySelector('.elem').innerHTML; //error if it's null
// document.querySelector('.elem')จะเป็น nullถ้าไม่เจอelement นั้น
let html = document.querySelector('.elem').innerHTML; //เกิด Error ถ้าเป็น null
```

Once again, if theelementdoesn'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 theerror and just accept `html = null` as the result.
อีกครั้ง ในหลายกรณีที่ไม่เจอelementก็ถือเป็นเรื่องปกติ เราอาจต้องการให้คืนค่า `html` เป็น `null` แทนที่จะเกิดerror

How can we do this?
## วิธีแก้ไขเดิมก่อนมี Optional Chaining

The obvious solution would be to check the value using`if`or the conditional operator `?`, before accessing itsproperty, like this:
ก่อนที่จะมี optional chaining `?.` เราต้องเช็คค่าด้วย`if`หรือ `&&` ก่อนที่จะเข้าถึงproperty ต่อๆ ไป เช่น:

```js
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.
หรือในกรณี `document.querySelector`:

E.g. let's try getting `user.address.street.name`.
```js run
let html = document.querySelector('.elem') ? document.querySelector('.elem').innerHTML : null;
```

We need to check both `user.address` and`user.address.street`:
ใช้ได้ แต่ดูยุ่งเหยิงและต้องเช็คซ้ำๆ โดยเฉพาะกับ property ที่ซ้อนกันหลายชั้น เช่น`user.address.street.name`

```js
let user = {}; //user has no address
let user = {}; //ผู้ใช้ไม่มีที่อยู่

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:
อีกวิธีที่ดูดีกว่าเล็กน้อยคือใช้ `&&`:

```js run
let user = {}; //user has no address
let user = {}; //ผู้ใช้ไม่มีที่อยู่

alert( user.address && user.address.street && user.address.street.name ); // undefined (no error)
alert( user.address && user.address.street && user.address.street.name ); // undefined (ไม่เกิด error)
```

AND'ing the whole path to thepropertyensures that all components exist (if not, the evaluation stops), but also isn't ideal.
แต่ก็ยังต้องเช็คpropertyซ้ำๆ อยู่ดี

As you can see, property names are still duplicated in the code. E.g. in the code above, `user.address` appears three times.
นี่จึงเป็นที่มาของ optional chaining `?.` เพื่อแก้ปัญหานี้ครั้งเดียวจบ!

That's why the optional chaining `?.` was added to the language. To solve this problem once and for all!
## Optional Chaining กับ Property: `?.`

##Optional chaining
Optional chaining `?.` จะหยุดการทำงานทันที ถ้าค่าทางซ้ายของ `?.` เป็น `undefined` หรือ `null` และจะคืนค่า `undefined` กลับมา

The optional chaining `?.` stops the evaluation if the value before `?.` is `undefined` or `null`and returns`undefined`.
**ในบทความนี้ เพื่อความกระชับ เราจะใช้คำว่า "มีอยู่" หมายถึงไม่เป็น `null`หรือ`undefined`**

**Further in this article, for brevity, we'll be saying that something "exists" if it's not `null` and not `undefined`.**
กล่าวคือ `value?.prop`:
- จะทำงานเหมือน `value.prop` ถ้า `value` มีอยู่
- ถ้า `value` เป็น `undefined/null` จะคืนค่า `undefined`

In other words, `value?.prop`:
- works as `value.prop`, if `value` exists,
- otherwise (when `value` is `undefined/null`) it returns `undefined`.

Here's the safe way to access `user.address.street` using `?.`:
ตัวอย่างการใช้ `?.` เพื่อเข้าถึง `user.address.street`:

```js run
let user = {}; //user has no address
let user = {}; //ผู้ใช้ไม่มีที่อยู่

alert( user?.address?.street ); // undefined (no error)
alert( user?.address?.street ); // undefined (ไม่เกิด error)
```

The code is short and clean, there's no duplication at all.
โค้ดสั้นลง และไม่มีการเช็คซ้ำๆ

กับตัวอย่าง `document.querySelector`:

Reading the address with `user?.address` works even if `user` object doesn't exist:
```js run
let html = document.querySelector('.elem')?.innerHTML; // จะเป็น undefined ถ้าไม่เจอ element
```

แม้แต่ในกรณีที่ไม่มีตัวแปร `user` เลย ก็ยังใช้ `?.` ได้อย่างปลอดภัย:

```js run
let user = null;
Expand All@@ -101,76 +100,70 @@ alert( user?.address ); // undefined
alert( user?.address.street ); // undefined
```

Please note: the`?.`syntax makesoptionalthe value before it, but not any further.
โปรดสังเกตว่า`?.`ทำให้ส่วนทางซ้ายมันเป็นoptionalได้ แต่ไม่ใช่ส่วนถัดไป

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 beoptional, then we'll need to replace more`.`with `?.`.
ใน`user?.address.street.name`ตัว `?.`อนุญาตให้ `user`เป็น`null/undefined`แต่ส่วนที่เหลือ (`address`, `street`, `name`) จะถูกเข้าถึงแบบปกติ ถ้าเราอยากให้ส่วนอื่นเป็นoptional ด้วย ต้องแทน`.`ด้วย `?.` เพิ่มเติม

```warn header="Don't overuse the optional chaining"
We should use`?.`only where it's ok that something doesn't exist.
```warn header="ใช้ ?. อย่างพอดี"
เราควรใช้`?.`เฉพาะในกรณีที่ยอมรับได้หากบางอย่างไม่มีอยู่

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`.
เช่น ถ้าตามตรรกะของโปรแกรมแล้ว`user`ต้องมีอยู่ แต่`address`เป็น optional ได้ เราควรเขียน`user.address?.street` แต่ไม่ใช่`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="The variable before `?.` must be declared"
If there's no variable`user` at all, then `user?.anything`triggers an error:
````warn header="ตัวแปรทางซ้ายของ ?. ต้องถูกประกาศก่อน"
ถ้าไม่มีตัวแปรนั้นเลย เช่น`user?.anything`จะเกิด error:

```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). Theoptional chainingworks only for declared variables.
ตัวแปรจะต้องถูกประกาศก่อน จากนั้นoptional chainingจึงจะใช้งานได้
````

## Short-circuiting

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

So, if there are any further function calls or side effects, they don't occur.

For instance:
`?.` จะหยุดการทำงานทันทีถ้าส่วนซ้ายไม่มีอยู่ (เป็น `null/undefined`) ดังนั้นถ้ามีการเรียก function หรือคำสั่งอื่นๆ ทางขวาของ `?.` จะไม่ถูกรันเลย

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

user?.sayHi(x++); //no "sayHi", so the execution doesn't reach x++
user?.sayHi(x++); //ไม่มี "user" จึงไม่ถึงการเรียก sayHi และ x++

alert(x); // 0,value not incremented
alert(x); // 0,ค่า x ไม่ถูกเพิ่ม
```

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

The optional chaining `?.` is not an operator, but a special syntax construct, that also works with functions and square brackets.
## Optional Chaining กับ Function: `?.()`

For example,`?.()`is used to call afunctionthat may not exist.
`?.()`ใช้เพื่อเรียกfunctionที่อาจจะไม่มีอยู่

In the code below, some of our users have`admin`method, and some don't:
ตัวอย่างเช่นในโค้ดด้านล่าง ผู้ใช้บางคนมี method`admin`บางคนไม่มี:

```js run
let userAdmin = {
admin() {
alert("I am admin");
alert("ฉันคือ admin");
}
};

let userGuest = {};

*!*
userAdmin.admin?.(); //I am admin
userAdmin.admin?.(); //ฉันคือ admin
*/!*

*!*
userGuest.admin?.(); //nothing (no suchmethod)
*!*
userGuest.admin?.(); //ไม่มีอะไรเกิดขึ้น (ไม่มีmethod admin)
*/!*
```

Here, in both lines we first use the dot (`userAdmin.admin`) to get`admin`property, because we assume that theuser object exists, so it's safe read from it.
สังเกตว่าเราใช้ `.` เพื่อเข้าถึง`admin`ก่อน เพราะเรามั่นใจว่าตัวแปร `user` มีอยู่แน่ๆ จากนั้นจึงตามด้วย `?.()`

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.
## Optional Chaining กับ [] : `?.[]`

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.
ถ้าเราต้องการใช้ `[]`เพื่อเข้าถึง property แทนที่จะใช้ `.` ก็ใช้ `?.[]` ได้

```js run
let key = "firstName";
Expand All@@ -179,42 +172,38 @@ let user1 = {
firstName: "John"
};

let user2 = null;
let user2 = null;

alert( user1?.[key] ); // John
alert( user2?.[key] ); // undefined
```

Also we can use `?.` with `delete`:
และสามารถใช้กับ `delete` ได้ด้วย:

```js run
delete user?.name; //delete user.nameif userexists
delete user?.name; //ลบ user.nameถ้า userมีอยู่
```

````warn header="We can use `?.` for safe reading and deleting, but not writing"
The optionalchaining `?.`has no use at the left side of an assignment.
````warn header="ใช้ ?. ได้แค่อ่านและลบ ไม่ใช่การเขียน"
Optionalchaining `?.`ไม่ใช้ในด้านซ้ายของการกำหนดค่า

For example:
```js run
let user = null;

user?.name = "John"; // Error, doesn't work
// because it evaluates to undefined = "John"
user?.name = "John"; // Error เพราะจะประเมินเป็น undefined = "John"
```

It's just not that smart.
````

##Summary
##สรุป

The optional chaining `?.`syntax has three forms:
ไวยากรณ์ Optional chaining `?.`มี 3 รูปแบบ:

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`.
1. `obj?.prop` --คืนค่า `obj.prop`ถ้า `obj`มีอยู่ ไม่งั้นคืน `undefined`
2. `obj?.[prop]` --คืนค่า `obj[prop]`ถ้า `obj`มีอยู่ ไม่งั้นคืน `undefined`
3. `obj.method?.()` --เรียก `obj.method()`ถ้า `obj.method`มีอยู่ ไม่งั้นคืน`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.
`?.`จะเช็ค`null/undefined`ทางด้านซ้าย และอนุญาตให้เข้าถึงต่อไปได้ถ้าไม่ใช่

A chain of`?.`allows to safely access nested properties.
การเรียงต่อกันหลายตัวของ`?.`ทำให้เข้าถึง property ที่ซ้อนกันได้อย่างปลอดภัย

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.
อย่างไรก็ตาม ควรใช้`?.`อย่างระมัดระวัง เฉพาะในกรณีที่ยอมรับได้หากบางอย่างไม่มีอยู่จริง เพื่อไม่ให้ข้อผิดพลาดที่ควรเกิดถูกซ่อนเงียบไป

[8]ページ先頭

©2009-2025 Movatter.jp