การออกแบบภาษา Javascript ไม่เข้มงวดเพียงพอ และอาจเกิดข้อผิดพลาดได้ในหลายที่หากคุณไม่ระวัง
ตัวอย่างเช่น พิจารณาสถานการณ์ต่อไปนี้
ตอนนี้ เราต้องตรวจสอบว่ามีอ็อบเจ็กต์ myObj อยู่หรือไม่ หากไม่มี ให้ประกาศมัน อัลกอริทึมที่อธิบายในภาษาธรรมชาติมีดังนี้:
คัดลอกรหัสรหัสดังต่อไปนี้:
ถ้า (myObj ไม่มีอยู่){
ประกาศ myObj;
-
คุณอาจคิดว่าการเขียนโค้ดนี้เป็นเรื่องง่าย แต่ในความเป็นจริงแล้ว ปัญหาทางไวยากรณ์ที่เกี่ยวข้องนั้นซับซ้อนกว่าที่เราคิดไว้มาก Juriy Zaytsev ชี้ให้เห็นว่ามีมากกว่า 50 วิธีในการพิจารณาว่ามีวัตถุ Javascript อยู่หรือไม่ เฉพาะในกรณีที่คุณมีความชัดเจนมากเกี่ยวกับรายละเอียดการใช้งานภาษา Javascript คุณจึงจะสามารถบอกความแตกต่างระหว่างภาษาเหล่านั้นได้
วิธีแรกของการเขียน
โดยสัญชาตญาณ คุณอาจคิดว่าคุณสามารถเขียนว่า:
คัดลอกรหัสรหัสดังต่อไปนี้:
ถ้า (!myObj) {
myObj = { };
-
อย่างไรก็ตาม เมื่อเรียกใช้โค้ดนี้ เบราว์เซอร์จะส่งข้อผิดพลาด ReferenceError โดยตรง ส่งผลให้การดำเนินการหยุดชะงัก เกิดอะไรขึ้น?
อย่างไรก็ตาม เมื่อคำสั่ง if กำหนดว่า myObj ว่างเปล่า ตัวแปรยังไม่มีอยู่ ดังนั้นจึงมีการรายงานข้อผิดพลาด เปลี่ยนเป็นรายการต่อไปนี้และมันจะทำงานได้อย่างถูกต้อง
คัดลอกรหัสรหัสดังต่อไปนี้:
ถ้า (!myObj) {
var myObj = { };
-
เหตุใดจึงไม่มีข้อผิดพลาดหลังจากเพิ่ม var? เป็นไปได้ไหมที่ในกรณีนี้ เมื่อคำสั่ง if ตัดสิน myObj มีอยู่แล้ว?
เพื่อตอบคำถามนี้ คุณต้องทราบวิธีการทำงานของล่าม Javascript ภาษา Javascript คือ "แยกวิเคราะห์ก่อน รันทีหลัง" การประกาศตัวแปรได้เสร็จสิ้นแล้วในระหว่างการแยกวิเคราะห์ ดังนั้นโค้ดข้างต้นจึงเทียบเท่ากับ:
คัดลอกรหัสรหัสดังต่อไปนี้:
var myObj;
ถ้า (!myObj) {
var myObj = { };
-
ดังนั้น เมื่อคำสั่ง if ทำการตัดสิน myObj ก็มีอยู่แล้ว ดังนั้นจึงไม่มีการรายงานข้อผิดพลาด นี่คือเอฟเฟกต์ "การยก" ของคำสั่ง var ล่าม Javascript จะ "เลื่อนระดับ" ตัวแปรที่กำหนดโดยคำสั่ง var เท่านั้น และใช้งานไม่ได้กับตัวแปรที่ได้รับมอบหมายโดยตรงโดยไม่ต้องใช้คำสั่ง var ด้วยเหตุนี้ระบบจะรายงานข้อผิดพลาดหากไม่ได้เพิ่ม var
วิธีที่สองของการเขียน
นอกจากคำสั่ง var แล้ว ยังมีการเขียนใหม่อีกประการหนึ่งที่สามารถรับผลลัพธ์ที่ถูกต้องได้:
คัดลอกรหัสรหัสดังต่อไปนี้:
ถ้า (!window.myObj) {
myObj = { };
-
Window เป็นออบเจ็กต์ระดับบนสุดของ JavaScript และตัวแปรโกลบอลทั้งหมดเป็นคุณสมบัติของมัน ดังนั้น การพิจารณาว่า myobj ว่างเปล่าจะเทียบเท่ากับการพิจารณาว่าวัตถุหน้าต่างมีแอตทริบิวต์ myobj หรือไม่ ดังนั้นจึงสามารถหลีกเลี่ยงข้อผิดพลาด ReferenceError ที่เกิดขึ้นเนื่องจากไม่ได้กำหนด myObj ได้ อย่างไรก็ตาม จากการกำหนดมาตรฐานของโค้ด วิธีที่ดีที่สุดคือเพิ่ม var ในบรรทัดที่สอง:
คัดลอกรหัสรหัสดังต่อไปนี้:
ถ้า (!window.myObj) {
var myObj = { };
-
หรือเขียนดังนี้:
ถ้า (!window.myObj) {
window.myObj = { };
-
วิธีที่สามในการเขียน
ข้อเสียของวิธีการเขียนข้างต้นคือในบางสภาพแวดล้อมที่ทำงานอยู่ (เช่น V8, Rhino) หน้าต่างอาจไม่ใช่วัตถุระดับบนสุด ดังนั้นให้ลองเขียนใหม่เป็น:
คัดลอกรหัสรหัสดังต่อไปนี้:
ถ้า (!this.myObj) {
นี้.myObj = { };
-
ที่ระดับตัวแปรร่วม คำสำคัญนี้จะชี้ไปที่ตัวแปรระดับบนสุดเสมอ ดังนั้นจึงเป็นอิสระจากสภาพแวดล้อมการทำงานที่แตกต่างกัน
วิธีที่สี่ในการเขียน
อย่างไรก็ตาม วิธีการเขียนข้างต้นอ่านได้ยาก และตัวชี้ของสิ่งนี้มีความแปรผันและเกิดข้อผิดพลาดได้ง่าย ดังนั้นเราจึงเขียนใหม่เพิ่มเติม:
คัดลอกรหัสรหัสดังต่อไปนี้:
var global = นี่;
ถ้า (!global.myObj) {
global.myObj = { };
-
การใช้ตัวแปรที่กำหนดเองทั่วโลกเพื่อแสดงออบเจ็กต์ระดับบนสุดจะชัดเจนกว่ามาก
วิธีที่ห้าในการเขียน
คุณยังสามารถใช้ตัวดำเนินการ typeof เพื่อพิจารณาว่า myObj ถูกกำหนดไว้หรือไม่
คัดลอกรหัสรหัสดังต่อไปนี้:
ถ้า (ประเภทของ myObj == "ไม่ได้กำหนด") {
var myObj = { };
-
ปัจจุบันนี้เป็นวิธีการที่ใช้กันอย่างแพร่หลายในการพิจารณาว่ามีวัตถุ JavaScript อยู่หรือไม่
วิธีที่หกในการเขียน
เนื่องจากค่าของ myObj เท่ากับไม่ได้กำหนดโดยตรงเมื่อมีการกำหนดแต่ไม่ได้กำหนด วิธีการเขียนข้างต้นจึงสามารถทำให้ง่ายขึ้น:
คัดลอกรหัสรหัสดังต่อไปนี้:
ถ้า (myObj == ไม่ได้กำหนด) {
var myObj = { };
-
มีสองสิ่งที่ควรทราบที่นี่ ประการแรก คีย์เวิร์ด var ในบรรทัดที่สองจะต้องไม่ขาดหายไป ไม่เช่นนั้นจะเกิดข้อผิดพลาดในการอ้างอิง ประการที่สอง ไม่สามารถเพิ่ม undef ด้วยเครื่องหมายคำพูดเดี่ยวหรือเครื่องหมายคำพูดคู่ได้ เนื่องจากมีการเปรียบเทียบประเภทข้อมูลของ undef ที่นี่ ไม่ใช่ "ไม่ได้กำหนด" สตริงนี้
วิธีที่เจ็ดในการเขียน
วิธีการเขียนข้างต้นยังคงเป็นจริงในกรณีของ "การเปรียบเทียบที่แน่นอน" (===):
คัดลอกรหัสรหัสดังต่อไปนี้:
ถ้า (myObj === ไม่ได้กำหนด) {
var myObj = { };
-
วิธีที่แปดในการเขียน
ตามการออกแบบภาษาของ JavaScript ไม่ได้กำหนด == null ดังนั้นการเปรียบเทียบว่า myObj เท่ากับ null ก็สามารถได้ผลลัพธ์ที่ถูกต้องเช่นกัน:
คัดลอกรหัสรหัสดังต่อไปนี้:
ถ้า (myObj == null) {
var myObj = { };
-
อย่างไรก็ตาม แม้ว่าผลการรันจะถูกต้อง แต่จากมุมมองเชิงความหมายแล้ว วิธีการตัดสินนี้ก็ผิดและควรหลีกเลี่ยง เนื่องจาก null อ้างอิงถึงอ็อบเจ็กต์ว่างที่ได้รับการกำหนดค่าเป็น null นั่นคืออ็อบเจ็กต์นี้มีค่าจริง ๆ ในขณะที่ undefed อ้างถึงอ็อบเจ็กต์ที่ไม่มีอยู่หรือไม่มีการกำหนดค่า ดังนั้น เฉพาะ "ตัวดำเนินการเปรียบเทียบ" (==) เท่านั้นที่สามารถใช้ได้ที่นี่ หากใช้ "ตัวดำเนินการเปรียบเทียบที่แน่นอน" (===) ที่นี่ ข้อผิดพลาดจะเกิดขึ้น
วิธีที่เก้าในการเขียน
คุณยังสามารถใช้ตัวดำเนินการ in เพื่อพิจารณาว่า myObj เป็นแอตทริบิวต์ของออบเจ็กต์ระดับบนสุดหรือไม่:
คัดลอกรหัสรหัสดังต่อไปนี้:
ถ้า (!('myObj' ในหน้าต่าง)) {
window.myObj = { };
-
วิธีที่สิบของการเขียน
สุดท้าย ใช้เมธอด hasOwnProperty เพื่อตรวจสอบว่า myObj เป็นคุณสมบัติของอ็อบเจ็กต์ระดับบนสุดหรือไม่:
คัดลอกรหัสรหัสดังต่อไปนี้:
ถ้า (!this.hasOwnProperty('myObj')) {
นี้.myObj = { };
-
สรุป
1. หากคุณเพียงแต่พิจารณาว่ามีวัตถุอยู่หรือไม่ ขอแนะนำให้ใช้วิธีการเขียนวิธีที่ห้า
2. หากนอกเหนือจากว่ามีวัตถุอยู่หรือไม่ คุณต้องพิจารณาว่าวัตถุนั้นมีค่าว่างหรือไม่ ขอแนะนำให้ใช้วิธีเขียนวิธีแรก
3. เว้นแต่จะมีสถานการณ์พิเศษ ควรประกาศตัวแปรทั้งหมดโดยใช้คำสั่ง var
4. เพื่อให้เป็นแบบข้ามแพลตฟอร์ม ขอแนะนำให้หลีกเลี่ยงการใช้หน้าต่างเพื่อแสดงวัตถุระดับบนสุด
5. ในภาษา Javascript ค่าว่างและค่าไม่ได้กำหนดจะสับสนได้ง่าย ในกรณีที่อาจเกี่ยวข้องทั้งสองอย่าง ขอแนะนำให้ใช้ตัวดำเนินการ "การเปรียบเทียบที่แน่นอน" (===)