JavaScript
Object
Optional chaining
ต่อยอดจาก nested objects เพื่อเข้าถึงข้อมูลที่อาจหายระหว่างทางได้อย่างปลอดภัยด้วย `?.` ทั้งการอ่าน nested property และการเรียก method ที่อาจไม่มี
ปัญหาหลังเรียน Nested Object
หลังจากเราเข้าถึง nested object ด้วย dot chaining ได้แล้ว ปัญหาที่เจอบ่อยมากคือ object ระหว่างทางอาจไม่มีอยู่จริง ถ้า chain ไปต่อทั้งที่ค่าระหว่างทางเป็น `undefined` หรือ `null` JavaScript จะหยุดด้วย error ทันที
ในตัวอย่างนี้ `profile.address` ไม่มีอยู่ แต่โค้ดยังพยายามอ่าน `.city` ต่อ
const user = {
profile: {
name: "Mali",
},
};
console.log(user.profile.name); // "Mali"
// ❌ TypeError: Cannot read properties of undefined
console.log(user.profile.address.city);ประเด็นสำคัญไม่ใช่ `city` หาย แต่คือ `address` หายก่อน พอ JavaScript จะอ่าน `.city` จาก `undefined` จึงเกิด error ทันที
วิธีเดิมที่ผู้เรียนรู้แล้ว
ก่อนมี `?.` เรามักป้องกันด้วย `if` หรือ `&&` เพื่อเช็กทีละชั้นก่อนเข้าถึงค่าลึก ๆ วิธีนี้ยังใช้ได้ และควรเข้าใจให้แม่น เพราะมันอธิบายหลักคิดเดียวกับ Optional Chaining
ทั้งสองแบบป้องกัน error ได้ แต่ยิ่ง object ลึก โค้ดยิ่งยาว
const user = {
profile: {
name: "Mali",
},
};
let cityFromIf;
if (user.profile && user.profile.address) {
cityFromIf = user.profile.address.city;
}
const cityFromAnd =
user.profile &&
user.profile.address &&
user.profile.address.city;
console.log(cityFromIf); // undefined
console.log(cityFromAnd); // undefinedแนวคิดของทั้งสองวิธีคือ "ถ้าชั้นก่อนหน้ายังมีอยู่ ค่อยไปต่อ" Optional Chaining ทำงานตามหลักเดียวกัน แต่เขียนสั้นและอ่านเส้นทางข้อมูลได้ชัดกว่า
`?.` ทำอะไร
`?.` คือ operator สำหรับเข้าถึง property อย่างปลอดภัย ถ้าค่าก่อนหน้าเป็น `null` หรือ `undefined` expression จะหยุดตรงนั้นและคืน `undefined` แทนการ throw error
Optional Chaining ทำให้เราอ่าน nested property ได้โดยไม่ต้อง guard ทุกชั้นด้วยมือ
const user = {
profile: {
name: "Mali",
},
};
const city = user.profile?.address?.city;
console.log(city); // undefined
const member = {
profile: {
name: "Tida",
address: {
city: "Bangkok",
},
},
};
console.log(member.profile?.address?.city); // "Bangkok"ให้มองว่า `?.` คือจุดเช็กระหว่างทาง ถ้าชั้นนั้นมีอยู่ก็ไปต่อ ถ้าไม่มีอยู่ก็หยุดและส่ง `undefined` กลับมา
ต้องใส่ทุกชั้นที่อาจหาย
Optional Chaining ไม่ได้ทำให้ path ทั้งเส้นปลอดภัยอัตโนมัติ เราต้องใส่ `?.` ในทุกชั้นที่ "อาจไม่มี" ถ้าข้ามชั้นที่เสี่ยงไป โค้ดยัง crash ได้เหมือนเดิม
| โค้ด | เกิดอะไรขึ้น | เพราะอะไร |
|---|---|---|
| `user?.address.city` | อาจ crash | `user` ปลอดภัย แต่ `address` ยังอาจเป็น undefined แล้วถูกอ่าน `.city` ต่อ |
| `user?.address?.city` | ปลอดภัย | ทั้ง `user` และ `address` ถูกเช็กก่อนอ่านชั้นถัดไป |
ตัวอย่างนี้ `address` เป็น `undefined` จึงต้องเช็กที่ชั้น `address` ด้วย
const user = {
name: "Korn",
};
// ❌ ยังเสี่ยง เพราะ address อาจไม่มี
// console.log(user?.address.city);
// ✅ ปลอดภัยกว่า เพราะเช็ก address ด้วย
console.log(user?.address?.city); // undefinedใช้กับ method ที่อาจไม่มีได้
นอกจากอ่าน property แล้ว `?.` ยังใช้ตอนเรียก method ที่อาจไม่มีอยู่ได้ด้วย เหมาะกับ object ที่บางครั้งมี helper function เพิ่มมา บางครั้งไม่มี
ถ้า `getDisplayName` ไม่มีอยู่ expression จะคืน `undefined` แทนการ throw error
const userA = {
profile: {
firstName: "Nina",
getDisplayName() {
return this.firstName.toUpperCase();
},
},
};
const userB = {
profile: {
firstName: "Mek",
},
};
console.log(userA.profile?.getDisplayName?.()); // "NINA"
console.log(userB.profile?.getDisplayName?.()); // undefinedรูปแบบนี้ช่วยได้มากเมื่อโค้ดต้องทำงานกับ object หลายรูปแบบ แต่เรายังไม่แน่ใจว่า method นั้นถูกใส่มาหรือไม่
เมื่อไหร่ควรใช้ และเมื่อไหร่ไม่ควรใช้
ควรใช้ `?.` เมื่อข้อมูลนั้น "อาจไม่มีจริง ๆ" เช่น field บางตัวจาก API, config ที่เป็น optional, หรือ nested object ที่ขึ้นกับสถานะผู้ใช้ ไม่ควรใช้เพื่อปิดปัญหาที่ควรแก้ เช่น function ควรคืน object แต่ดันคืน `undefined` เพราะ bug ใน logic
| สถานการณ์ | ควรใช้ `?.` ไหม | เหตุผล |
|---|---|---|
| API อาจไม่ส่ง `address` มา | ควรใช้ | ข้อมูลอาจหายได้จริงตามเงื่อนไขของระบบ |
| object ควรถูกสร้างครบทุกครั้ง | ไม่ควรใช้เพื่อกลบ bug | ควรหาสาเหตุว่าทำไมข้อมูลถึงหาย |
| method บาง object มี บาง object ไม่มี | ควรใช้ | ช่วยเรียก method แบบปลอดภัยโดยไม่ต้อง if ซ้ำ |
บทถัดไปในสาย modern syntax ยังมี operator ที่มักใช้คู่กับ `?.` คือ `??` สำหรับใส่ค่า fallback เมื่อผลลัพธ์เป็น `null` หรือ `undefined` แต่ในบทนี้ให้โฟกัสที่การเข้าถึงข้อมูลอย่างปลอดภัยก่อน