คุณทราบหรือไม่ว่าองค์ประกอบ DOM ที่มี ID สามารถเข้าถึงได้ใน JavaScript เป็นตัวแปรส่วนกลาง มันเป็นหนึ่งในสิ่งเหล่านั้นที่อยู่รอบตัวเช่นตลอดไป แต่ฉันขุดค้นมันเป็นครั้งแรกจริงๆ
หากนี่เป็นครั้งแรกที่คุณได้ยินเกี่ยวกับเรื่องนี้ เตรียมตัวให้พร้อม! เราสามารถเห็นมันในการดำเนินการได้ง่ายๆ โดยการเพิ่ม ID ให้กับองค์ประกอบใน HTML:
โดยปกติ เราจะกำหนดตัวแปรใหม่โดยใช้ querySelector("#cool")
or getElementById("cool")
เพื่อเลือกองค์ประกอบนั้น:
var el = querySelector("#cool");
แต่เรามีสิทธิ์เข้าถึง .แล้ว #cool
โดยไม่มีพื้นฐานนั้น:
ดังนั้นใดๆ id
- หรือ name
แอตทริบิวต์สำหรับเรื่องนั้น - ใน HTML สามารถเข้าถึงได้ใน JavaScript โดยใช้ window[ELEMENT_ID]
. อีกครั้ง นี่ไม่ใช่ "ใหม่" อย่างแน่นอน แต่เป็นเรื่องปกติที่จะเห็น
อย่างที่คุณอาจเดาได้ การเข้าถึงขอบเขตทั่วโลกด้วยการอ้างอิงที่มีชื่อไม่ใช่แนวคิดที่ยิ่งใหญ่ที่สุด บางคนเรียกสิ่งนี้ว่า "ผู้ก่อมลพิษในขอบเขตทั่วโลก" เราจะเข้าใจว่าทำไมถึงเป็นเช่นนั้น แต่ก่อนอื่น…
บริบทบางอย่าง
แนวทางนี้คือ ระบุไว้ในข้อกำหนด HTMLซึ่งอธิบายว่าเป็น “การเข้าถึงที่มีชื่อบน Window
วัตถุ."
Internet Explorer เป็นคนแรกที่ใช้คุณลักษณะนี้ เบราว์เซอร์อื่น ๆ ทั้งหมดก็เพิ่มเช่นกัน ตุ๊กแกเป็นเบราว์เซอร์เพียงตัวเดียวในขณะนั้นที่ไม่สนับสนุนโดยตรงในโหมดมาตรฐาน โดยเลือกที่จะทำให้มันเป็นคุณลักษณะทดลองแทน มีความลังเลที่จะนำไปใช้เลย แต่ก็ ก้าวไปข้างหน้าในนามของความเข้ากันได้ของเบราว์เซอร์ (ตุ๊กแกก็พยายาม โน้มน้าว WebKit เพื่อย้ายออกจากโหมดมาตรฐาน) และในที่สุดก็เข้าสู่โหมดมาตรฐานใน Firefox 14
สิ่งหนึ่งที่อาจไม่เป็นที่ทราบกันดีนักคือเบราว์เซอร์ต้องมีมาตรการป้องกันไว้บ้าง — ด้วยระดับความสำเร็จที่แตกต่างกัน — เพื่อให้แน่ใจว่า globals ที่สร้างขึ้นจะไม่ทำให้หน้าเว็บเสียหาย หนึ่งในมาตรการดังกล่าวคือ...
เงาตัวแปร
ส่วนที่น่าสนใจที่สุดของคุณลักษณะนี้คือการอ้างอิงองค์ประกอบที่มีชื่อไม่ เงาตัวแปรโกลบอลที่มีอยู่. ดังนั้น หากองค์ประกอบ DOM มี id
ที่ถูกกำหนดให้เป็นโกลบอล จะไม่แทนที่ที่มีอยู่ ตัวอย่างเช่น:
window.foo = "bar";
I won't override window.foo
console.log(window.foo); // Prints "bar"
และตรงกันข้ามก็เป็นจริงเช่นกัน:
I will be overridden :(
window.foo = "bar";
console.log(window.foo); // Prints "bar"
ลักษณะการทำงานนี้มีความสำคัญเนื่องจากจะลบล้างการแทนที่ที่เป็นอันตรายเช่น
ซึ่งมิฉะนั้นจะสร้างความขัดแย้งโดยการทำให้ .เป็นโมฆะ alert
เอพีไอ เทคนิคการป้องกันนี้อาจเป็นเหตุผลที่คุณ — ถ้าคุณเป็นเหมือนฉัน — กำลังเรียนรู้เกี่ยวกับสิ่งนี้เป็นครั้งแรก
คดีที่มีชื่อ globals
ก่อนหน้านี้ ฉันกล่าวว่าการใช้องค์ประกอบที่มีชื่อทั่วโลกเป็นข้อมูลอ้างอิงอาจไม่ใช่แนวคิดที่ยิ่งใหญ่ที่สุด มีเหตุผลมากมายที่ TJ VanToll ได้กล่าวถึงบล็อกของเขาอย่างดี และฉันจะสรุปที่นี่:
- หาก DOM เปลี่ยนแปลง ข้อมูลอ้างอิงก็เช่นกัน นั่นทำให้บางอย่าง "เปราะ" (ข้อกำหนดของข้อกำหนด สำหรับมัน) รหัสที่การแยกข้อกังวลระหว่าง HTML และ JavaScript อาจมากเกินไป
- การอ้างอิงโดยบังเอิญนั้นง่ายเกินไป การพิมพ์ผิดอย่างง่ายอาจจบลงด้วยการอ้างอิงโกลบอลที่มีชื่อและให้ผลลัพธ์ที่ไม่คาดคิด
- มีการใช้งานแตกต่างกันในเบราว์เซอร์ ตัวอย่างเช่น เราควรเข้าถึงสมอด้วย an
id
- เช่น— แต่เบราว์เซอร์บางตัว (เช่น Safari และ Firefox) ส่งคืน a
ReferenceError
ในคอนโซล - อาจไม่คืนสิ่งที่คุณคิด ตามข้อมูลจำเพาะ เมื่อมีหลายอินสแตนซ์ขององค์ประกอบที่มีชื่อเดียวกันใน DOM — พูด สองอินสแตนซ์ของ
— เบราว์เซอร์ควรส่งคืน an
HTMLCollection
กับอาร์เรย์ของอินสแตนซ์ อย่างไรก็ตาม Firefox จะส่งคืนเฉพาะอินสแตนซ์แรกเท่านั้น แล้วอีกครั้ง สเปกบอกว่า เราควรใช้ตัวอย่างหนึ่งของ anid
ในต้นไม้ขององค์ประกอบอยู่แล้ว แต่การทำเช่นนี้จะไม่ทำให้เพจทำงานหรืออะไรแบบนั้นได้ - อาจมีค่าใช้จ่ายด้านประสิทธิภาพ? ฉันหมายความว่าเบราว์เซอร์ต้องทำรายการอ้างอิงและบำรุงรักษา สองสามคนทำการทดสอบ ในเธรด StackOverflow นี้โดยที่ชื่อ globals เป็นจริง มีประสิทธิภาพมากขึ้นในการทดสอบเดียว และ ประสิทธิภาพน้อยกว่าในการทดสอบล่าสุด.
ข้อควรพิจารณาเพิ่มเติม
สมมติว่าเราโยนคำวิพากษ์วิจารณ์ว่าใช้ globals ที่มีชื่อแล้วใช้ต่อไป มันเป็นเรื่องดีทั้งหมด. แต่มีบางสิ่งที่คุณอาจต้องการพิจารณาเมื่อคุณทำ
โพลีฟิล
การตรวจสอบสากลประเภทนี้เป็นข้อกำหนดในการตั้งค่าทั่วไปสำหรับโพลีฟิล ดูตัวอย่างต่อไปนี้ที่เราตั้งค่าคุกกี้โดยใช้ new CookieStore
API, เติมลงในเบราว์เซอร์ที่ยังไม่รองรับ:
// Polyfill the CookieStore API if not yet implemented.
// https://developer.mozilla.org/en-US/docs/Web/API/CookieStore
if (!window.cookieStore) {
window.cookieStore = myCookieStorePolyfill;
}
cookieStore.set("foo", "bar");
รหัสนี้ใช้งานได้ดีใน Chrome แต่แสดงข้อผิดพลาดต่อไปนี้ใน Safari:
TypeError: cookieStore.set is not a function
Safari ขาดการสนับสนุนสำหรับ CookieStore
API ณ วันที่เขียนนี้ จึงไม่มีการใช้โพลีฟิล เนื่องจาก img
รหัสองค์ประกอบสร้างตัวแปรส่วนกลางที่ขัดแย้งกับ cookieStore
ทั่วโลก
อัปเดต JavaScript API
เราสามารถพลิกสถานการณ์และพบปัญหาอื่นที่การอัปเดตกลไก JavaScript ของเบราว์เซอร์สามารถทำลายการอ้างอิงทั่วโลกขององค์ประกอบที่มีชื่อได้
ตัวอย่างเช่น:
window.BarcodeDetector.focus();
สคริปต์นั้นดึงการอ้างอิงไปยังองค์ประกอบอินพุตและเรียกใช้ focus()
เกี่ยวกับมัน มันทำงานอย่างถูกต้อง ยังไงก็ไม่รู้ ยาว มันจะทำงานต่อไป
คุณเห็นไหมว่าตัวแปรส่วนกลางที่เราใช้เพื่ออ้างอิงองค์ประกอบอินพุตจะหยุดทำงานทันทีที่เบราว์เซอร์เริ่มรองรับ BarcodeDetector
API. ณ จุดนั้น window.BarcodeDetector
global จะไม่อ้างอิงถึงองค์ประกอบอินพุตและ .อีกต่อไป .focus()
จะขว้าง“window.BarcodeDetector.focus
ไม่ใช่ข้อผิดพลาดของฟังก์ชัน
โบนัส: องค์ประกอบที่มีชื่อบางรายการเท่านั้นที่สร้างการอ้างอิงทั่วโลก
ต้องการที่จะได้ยินเรื่องตลก? เพื่อเพิ่มการดูถูกการบาดเจ็บ องค์ประกอบที่มีชื่อสามารถเข้าถึงได้เป็นตัวแปรส่วนกลางก็ต่อเมื่อชื่อนั้นไม่มีอะไรนอกจากตัวอักษร เบราว์เซอร์จะไม่สร้างการอ้างอิงทั่วโลกสำหรับองค์ประกอบที่มี ID ที่มีอักขระพิเศษและตัวเลข เช่น hello-world
และ item1
.
สรุป
สรุปว่าเรามาที่นี่ได้อย่างไร:
- เบราว์เซอร์หลักทั้งหมดสร้างการอ้างอิงทั่วโลกไปยังแต่ละองค์ประกอบ DOM ด้วย an
id
(หรือในบางกรณี aname
คุณลักษณะ). - การเข้าถึงองค์ประกอบเหล่านี้ผ่านการอ้างอิงทั่วโลกนั้นไม่น่าเชื่อถือและอาจเป็นอันตรายได้ ใช้
querySelector
orgetElementById
แทน. - เนื่องจากการอ้างอิงทั่วโลกถูกสร้างขึ้นโดยอัตโนมัติ จึงอาจมีผลข้างเคียงบางอย่างกับโค้ดของคุณ นั่นเป็นเหตุผลที่ดีที่จะหลีกเลี่ยงการใช้
id
คุณลักษณะเว้นแต่คุณต้องการจริงๆ
ท้ายที่สุด อาจเป็นความคิดที่ดีที่จะหลีกเลี่ยงการใช้ชื่อ globals ใน JavaScript ฉันอ้างอิงข้อมูลจำเพาะก่อนหน้านี้เกี่ยวกับวิธีการที่นำไปสู่รหัส "เปราะบาง" แต่นี่คือข้อความเต็มเพื่อขับเคลื่อนจุดกลับบ้าน:
ตามกฎทั่วไป การพึ่งพาสิ่งนี้จะทำให้โค้ดเปราะบาง รหัสใดที่เชื่อมโยงกับ API นี้สามารถเปลี่ยนแปลงได้เมื่อเวลาผ่านไป เนื่องจากมีการเพิ่มคุณสมบัติใหม่ลงในแพลตฟอร์มเว็บ เป็นต้น ให้ใช้ .แทน
document.getElementById()
ordocument.querySelector()
.
ฉันคิดว่าความจริงที่ว่าข้อมูลจำเพาะ HTML แนะนำให้อยู่ห่างจากคุณลักษณะนี้พูดสำหรับตัวเอง