นอกจากนี้ล่าสุด
นี่เป็นส่วนเพิ่มเติมล่าสุดของภาษา เบราว์เซอร์รุ่นเก่าอาจต้องใช้โพลีฟิล
การผูกมัดเสริม ?.
เป็นวิธีที่ปลอดภัยในการเข้าถึงคุณสมบัติของอ็อบเจ็กต์ที่ซ้อนกัน แม้ว่าจะไม่มีคุณสมบัติระดับกลางก็ตาม
หากคุณเพิ่งเริ่มอ่านบทช่วยสอนและเรียนรู้ JavaScript ปัญหาอาจยังไม่เกิดขึ้นกับคุณ แต่เป็นเรื่องปกติ
ตัวอย่างเช่น สมมติว่าเรามีออบเจ็กต์ user
ที่เก็บข้อมูลเกี่ยวกับผู้ใช้ของเรา
ผู้ใช้ส่วนใหญ่ของเรามีที่อยู่ในคุณสมบัติ user.address
พร้อมด้วย street user.address.street
แต่บางรายไม่ได้ระบุไว้
ในกรณีเช่นนี้ เมื่อเราพยายามรับ user.address.street
และผู้ใช้ไม่มีที่อยู่ เราได้รับข้อผิดพลาด:
ให้ผู้ใช้ = {}; // ผู้ใช้ที่ไม่มีคุณสมบัติ "ที่อยู่" การแจ้งเตือน (user.address.street); // ข้อผิดพลาด!
นั่นคือผลลัพธ์ที่คาดหวัง จาวาสคริปต์ทำงานเช่นนี้ เนื่องจาก user.address
undefined
ความพยายามในการรับ user.address.street
จึงล้มเหลวโดยมีข้อผิดพลาด
ในทางปฏิบัติหลายๆ กรณี เราอยากได้ undefined
แทนที่จะแสดงข้อผิดพลาด (แปลว่า "ไม่มีถนน")
…และอีกตัวอย่างหนึ่ง ในการพัฒนาเว็บ เราสามารถรับออบเจ็กต์ที่สอดคล้องกับองค์ประกอบของหน้าเว็บโดยใช้การเรียกเมธอดพิเศษ เช่น document.querySelector('.elem')
และจะส่งคืนค่า null
เมื่อไม่มีองค์ประกอบดังกล่าว
// document.querySelector('.elem') จะเป็นโมฆะหากไม่มีองค์ประกอบ ให้ html = document.querySelector('.elem').innerHTML; // ผิดพลาดถ้ามันเป็นโมฆะ
อีกครั้ง หากไม่มีองค์ประกอบนี้ เราจะได้รับข้อผิดพลาดในการเข้าถึงคุณสมบัติ .innerHTML
ของ null
และในบางกรณี เมื่อการไม่มีองค์ประกอบเป็นเรื่องปกติ เราต้องการหลีกเลี่ยงข้อผิดพลาดและยอมรับผลลัพธ์ html = null
เราจะทำเช่นนี้ได้อย่างไร?
ทางออกที่ชัดเจนคือการตรวจสอบค่าโดยใช้ if
หรือตัวดำเนินการแบบมีเงื่อนไข ?
ก่อนที่จะเข้าถึงคุณสมบัติของตนเช่นนี้:
ให้ผู้ใช้ = {}; การแจ้งเตือน (user.address ? user.address.street : ไม่ได้กำหนด);
มันใช้งานได้ ไม่มีข้อผิดพลาด… แต่มันค่อนข้างไม่สวยงาม อย่างที่คุณเห็น "user.address"
ปรากฏขึ้นสองครั้งในโค้ด
document.querySelector
:
ให้ html = document.querySelector('.elem') ? document.querySelector('.elem').innerHTML : null;
เราจะเห็นได้ว่าการค้นหาองค์ประกอบ document.querySelector('.elem')
จริงๆ แล้วถูกเรียกสองครั้งที่นี่ ไม่ดี.
สำหรับคุณสมบัติที่ซ้อนกันลึกยิ่งขึ้น จะยิ่งดูน่าเกลียดยิ่งขึ้น เนื่องจากจำเป็นต้องทำซ้ำมากขึ้น
เช่น มารับ user.address.street.name
ในลักษณะเดียวกัน
ให้ผู้ใช้ = {}; // ผู้ใช้ไม่มีที่อยู่ การแจ้งเตือน (user.address ? user.address.street ? user.address.street.name : null : null);
แย่มาก เราอาจมีปัญหาในการทำความเข้าใจโค้ดดังกล่าวด้วยซ้ำ
มีวิธีเขียนที่ดีกว่านี้เล็กน้อย โดยใช้ตัวดำเนินการ &&
:
ให้ผู้ใช้ = {}; // ผู้ใช้ไม่มีที่อยู่ การแจ้งเตือน ( user.address && user.address.street && user.address.street.name ); // ไม่ได้กำหนด (ไม่มีข้อผิดพลาด)
และเส้นทางทั้งหมดไปยังคุณสมบัติทำให้มั่นใจได้ว่าส่วนประกอบทั้งหมดมีอยู่ (หากไม่ การประเมินจะหยุดลง) แต่ก็ไม่เหมาะเช่นกัน
อย่างที่คุณเห็น ชื่อคุณสมบัติยังคงซ้ำกันในโค้ด เช่นในโค้ดด้านบน user.address
จะปรากฏขึ้นสามครั้ง
นั่นเป็นเหตุผลว่าทำไมการผูกมัดเสริม ?.
ถูกเพิ่มเข้าไปในภาษา เพื่อแก้ไขปัญหานี้ให้หมดไป!
การผูกมัดเสริม ?.
หยุดการประเมินหากค่าอยู่ก่อนหน้า ?.
เป็น undefined
หรือ null
และส่งคืน undefined
นอกจากนี้ ในบทความนี้ เพื่อความกระชับ เราจะพูดถึงบางสิ่งที่ “มีอยู่” ถ้ามันไม่เป็น null
และไม่ใช่ undefined
กล่าวอีกนัยหนึ่ง value?.prop
:
ทำงานเป็น value.prop
หากมี value
อยู่
มิฉะนั้น (เมื่อ value
เป็น undefined/null
) ก็จะส่งคืน undefined
นี่เป็นวิธีที่ปลอดภัยในการเข้าถึง user.address.street
โดยใช้ ?.
-
ให้ผู้ใช้ = {}; // ผู้ใช้ไม่มีที่อยู่ การแจ้งเตือน ( ผู้ใช้?.ที่อยู่?.ถนน ); // ไม่ได้กำหนด (ไม่มีข้อผิดพลาด)
รหัสสั้นและสะอาด ไม่มีการซ้ำซ้อนเลย
นี่คือตัวอย่างที่มี document.querySelector
:
ให้ html = document.querySelector('.elem')?.innerHTML; // จะไม่ถูกกำหนด หากไม่มีองค์ประกอบ
การอ่านที่อยู่ด้วย user?.address
ใช้งานได้แม้ว่าจะไม่มีวัตถุ user
ก็ตาม:
ให้ผู้ใช้ = null; การแจ้งเตือน( ผู้ใช้?.ที่อยู่ ); // ไม่ได้กำหนด การแจ้งเตือน ( ผู้ใช้?.address.street ); // ไม่ได้กำหนด
โปรดทราบ: ?.
ไวยากรณ์ทำให้ค่าที่ไม่บังคับอยู่ข้างหน้า แต่ไม่เพิ่มเติมอีกต่อไป
เช่นใน user?.address.street.name
?.
อนุญาตให้ user
เป็น null/undefined
อย่างปลอดภัย (และส่งคืน undefined
ในกรณีนั้น) แต่นั่นสำหรับ user
เท่านั้น เข้าถึงคุณสมบัติเพิ่มเติมได้ตามปกติ หากเราต้องการให้บางส่วนเป็นทางเลือก เราก็จะต้องแทนที่ .
กับ ?.
-
อย่าใช้การต่อโซ่เสริมมากเกินไป
เราควรใช้ ?.
เฉพาะในกรณีที่ไม่มีอะไรเกิดขึ้น
ตัวอย่างเช่น หากออบเจ็กต์ user
ตามลอจิกโค้ดของเราต้องมีอยู่ แต่ address
เป็นทางเลือก เราควรเขียน user.address?.street
แต่ไม่ใช่ user?.address?.street
จากนั้น หาก user
ไม่ได้กำหนดไว้ เราจะเห็นข้อผิดพลาดในการเขียนโปรแกรมและแก้ไข ไม่อย่างนั้นถ้าเราใช้มากเกินไป ?.
ข้อผิดพลาดในการเขียนโค้ดสามารถถูกปิดเสียงได้เมื่อไม่เหมาะสม และแก้ไขจุดบกพร่องได้ยากขึ้น
ตัวแปรก่อนหน้า ?.
จะต้องประกาศ
หากไม่มีตัวแปร user
เลย ดังนั้น user?.anything
จะทำให้เกิดข้อผิดพลาด:
// ReferenceError: ไม่ได้กำหนดผู้ใช้ ผู้ใช้?.ที่อยู่;
ตัวแปรจะต้องได้รับการประกาศ (เช่น let/const/var user
หรือเป็นพารามิเตอร์ฟังก์ชัน) การผูกมัดทางเลือกใช้ได้กับตัวแปรที่ประกาศเท่านั้น
ตามที่กล่าวไว้ก่อนหน้านี้ ?.
หยุดการประเมิน (“ลัดวงจร”) ทันที หากไม่มีชิ้นส่วนด้านซ้าย
ดังนั้น หากมีการเรียกใช้ฟังก์ชันเพิ่มเติมหรือการดำเนินการใดๆ ทางด้านขวาของ ?.
พวกมันจะไม่ถูกสร้างขึ้น
ตัวอย่างเช่น:
ให้ผู้ใช้ = null; ให้ x = 0; ผู้ใช้?.sayHi(x++); // ไม่มี "ผู้ใช้" ดังนั้นการดำเนินการจึงไปไม่ถึงการเรียก sayHi และ x++ การแจ้งเตือน(x); // 0 ค่าไม่เพิ่มขึ้น
การผูกมัดเสริม ?.
ไม่ใช่ตัวดำเนินการ แต่เป็นโครงสร้างไวยากรณ์พิเศษที่ใช้ได้กับฟังก์ชันและวงเล็บเหลี่ยมด้วย
ตัวอย่างเช่น ?.()
ใช้เพื่อเรียกใช้ฟังก์ชันที่อาจไม่มีอยู่
ในโค้ดด้านล่าง ผู้ใช้ของเราบางคนมีวิธี admin
และบางคนไม่มี:
ให้ userAdmin = { ผู้ดูแลระบบ() { alert("ฉันเป็นผู้ดูแลระบบ"); - - ให้ userGuest = {}; userAdmin.admin?.(); // ฉันเป็นผู้ดูแลระบบ userGuest.admin?.(); // ไม่มีอะไรเกิดขึ้น (ไม่มีวิธีการดังกล่าว)
ในทั้งสองบรรทัดนี้ อันดับแรกเราใช้จุด ( userAdmin.admin
) เพื่อรับคุณสมบัติ admin
เนื่องจากเราถือว่ามีวัตถุ user
อยู่ ดังนั้นจึงอ่านได้อย่างปลอดภัย
จากนั้น ?.()
ตรวจสอบส่วนด้านซ้าย: หากมีฟังก์ชัน admin
อยู่ฟังก์ชันจะทำงาน (นั่นก็เป็นเช่นนั้นสำหรับ userAdmin
) มิฉะนั้น (สำหรับ userGuest
) การประเมินจะหยุดโดยไม่มีข้อผิดพลาด
ไวยากรณ์ ?.[]
ยังใช้งานได้ หากเราต้องการใช้วงเล็บ []
เพื่อเข้าถึงคุณสมบัติแทน .
- เช่นเดียวกับกรณีก่อนหน้านี้ ช่วยให้สามารถอ่านคุณสมบัติจากออบเจ็กต์ที่อาจไม่มีอยู่ได้อย่างปลอดภัย
ให้คีย์ = "ชื่อ"; ให้ผู้ใช้ 1 = { ชื่อจริง: "จอห์น" - ให้ user2 = null; การแจ้งเตือน ( user1?.[คีย์] ); // จอห์น การแจ้งเตือน ( user2?.[คีย์] ); // ไม่ได้กำหนด
นอกจากนี้เรายังสามารถใช้ ?.
ด้วย delete
:
ลบผู้ใช้?.ชื่อ; // ลบ user.name หากมีผู้ใช้อยู่
เราสามารถใช้ ?.
เพื่อการอ่านและการลบอย่างปลอดภัย แต่ไม่ใช่การเขียน
การผูกมัดเสริม ?.
ไม่มีประโยชน์ทางด้านซ้ายของงาน
ตัวอย่างเช่น:
ให้ผู้ใช้ = null; ผู้ใช้?.name = "จอห์น"; // เกิดข้อผิดพลาด ใช้งานไม่ได้ // เพราะมันประเมินเป็น: undef = "John"
การผูกมัดเสริม ?.
ไวยากรณ์มีสามรูปแบบ:
obj?.prop
– ส่งคืน obj.prop
หากมี obj
อยู่ มิฉะนั้น undefined
obj?.[prop]
– ส่งคืน obj[prop]
หากมี obj
อยู่ มิฉะนั้น undefined
obj.method?.()
– เรียก obj.method()
หากมี obj.method
อยู่ มิฉะนั้นจะส่งคืน undefined
ดังที่เราเห็นทั้งหมดนี้มีความตรงไปตรงมาและใช้งานง่าย ?.
ตรวจสอบส่วนด้านซ้ายว่าเป็น null/undefined
และอนุญาตให้การประเมินดำเนินการต่อหากไม่เป็นเช่นนั้น
ห่วงโซ่ของ ?.
ช่วยให้สามารถเข้าถึงคุณสมบัติที่ซ้อนกันได้อย่างปลอดภัย
ยังไงก็ควรสมัคร ?.
อย่างระมัดระวัง เฉพาะในกรณีที่เป็นที่ยอมรับตามตรรกะของโค้ดของเราเท่านั้นว่าไม่มีส่วนด้านซ้าย เพื่อไม่ให้ซ่อนข้อผิดพลาดในการเขียนโปรแกรมจากเราหากเกิดขึ้น