Ali ste vedeli, da so elementi DOM z ID-ji dostopni v JavaScriptu kot globalne spremenljivke? To je ena tistih stvari, ki so prisotne že od nekdaj, vendar se prvič zares poglobim v to.
Če prvič slišite za to, se pripravite! Delovanje lahko vidimo preprosto tako, da elementu v HTML dodamo ID:
Običajno bi novo spremenljivko definirali z uporabo querySelector("#cool")
or getElementById("cool")
da izberete ta element:
var el = querySelector("#cool");
Toda dejansko že imamo dostop do #cool
brez te rigamorale:
Torej, katerikoli id
- ali name
kar zadeva atribut – v HTML-ju lahko dostopate v JavaScriptu z uporabo window[ELEMENT_ID]
. Še enkrat, to ni ravno "novo", vendar je res neobičajno videti.
Kot morda ugibate, dostop do globalnega obsega z imenovanimi referencami ni najboljša ideja. Nekateri so to poimenovali »globalni onesnaževalec«. Ugotovili bomo, zakaj je tako, a najprej …
Nekaj konteksta
Ta pristop je opisano v specifikaciji HTML, kjer je opisan kot »imenovani dostop na Window
objekt."
Internet Explorer je prvi implementiral to funkcijo. Dodali so ga tudi vsi drugi brskalniki. Gecko je bil v tistem času edini brskalnik, ki ga ni podpiral neposredno v standardnem načinu, temveč se je odločil, da bo postal poskusna funkcija. Bilo je oklevanja, da bi ga sploh uveljavili, a je premaknil naprej v imenu združljivosti brskalnikov (Gecko je celo poskušal prepričati WebKit da ga premaknete iz standardnega načina) in sčasoma prešli v standardni način v Firefoxu 14.
Ena stvar, ki morda ni dobro znana, je, da so morali brskalniki uvesti nekaj previdnostnih ukrepov – z različnimi stopnjami uspeha – da zagotovijo, da ustvarjeni globali ne pokvarijo spletne strani. Eden takih ukrepov je…
Spremenljivo senčenje
Verjetno najbolj zanimiv del te funkcije je, da sklice na poimenovane elemente ne zasenči obstoječe globalne spremenljivke. Torej, če ima element DOM id
ki je že definiran kot globalni, ne bo preglasil obstoječega. Na primer:
window.foo = "bar";
I won't override window.foo
console.log(window.foo); // Prints "bar"
In tudi obratno velja:
I will be overridden :(
window.foo = "bar";
console.log(window.foo); // Prints "bar"
To vedenje je bistveno, ker izniči nevarne preglasitve, kot je npr
, kar bi sicer povzročilo konflikt z razveljavitvijo alert
API. Ta zaščitna tehnika je morda prav razlog, zakaj - če ste takšni kot jaz - prvič izveste o tem.
Zadeva proti imenovanim globalom
Prej sem rekel, da uporaba globalnih poimenovanih elementov kot referenc morda ni najboljša ideja. Za to obstaja veliko razlogov, ki TJ VanToll je lepo zapisal na svojem blogu in tukaj bom povzel:
- Če se spremeni DOM, se spremeni tudi sklic. Zaradi tega so nekateri res "krhki" (termin spec za to) kodo, pri kateri je lahko ločevanje pomislekov med HTML in JavaScript preveliko.
- Naključna sklicevanja so preveč enostavna. Preprosta tipkarska napaka se lahko zelo dobro konča s sklicevanjem na poimenovan global in vam da nepričakovane rezultate.
- V brskalnikih je implementiran drugače. Na primer, morali bi imeti možnost dostopa do sidra z
id
- npr— vendar nekateri brskalniki (in sicer Safari in Firefox) vrnejo a
ReferenceError
v konzoli. - Morda ne bo vrnilo tega, kar mislite. V skladu s specifikacijo, ko je v DOM več primerkov istega poimenovanega elementa – recimo dva primerka
— brskalnik bi moral vrniti an
HTMLCollection
z nizom primerkov. Firefox pa vrne samo prvo instanco. Ampak, specifikacija pravi morali bi uporabiti en primerek anid
tako ali tako v drevesu elementa. Toda s tem ne boste preprečili delovanja strani ali česa podobnega. - Morda obstaja strošek uspešnosti? Mislim, brskalnik mora narediti ta seznam referenc in ga vzdrževati. Nekaj ljudi je opravilo teste v tej niti StackOverflow, kjer so bili dejansko imenovani globali bolj zmogljiv v enem testu in manj zmogljiv v novejšem testu.
Dodatni premisleki
Recimo, da zavržemo kritike proti uporabi poimenovanih globalov in jih vseeno uporabimo. Vse je v redu. Obstaja pa nekaj stvari, ki bi jih morda želeli upoštevati.
Polifili
Naj se sliši še tako nenavadno, so te vrste globalnih preverjanj tipična zahteva za nastavitev polifillov. Oglejte si naslednji primer, kjer piškotek nastavimo z uporabo new CookieStore
API, izpolnjevanje v brskalnikih, ki tega še ne podpirajo:
// 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");
Ta koda deluje popolnoma v redu v Chromu, vendar vrže to napako v Safariju:
TypeError: cookieStore.set is not a function
Safari nima podpore za CookieStore
API od tega pisanja. Posledično se polifil ne nanese, ker img
element ID ustvari globalno spremenljivko, ki je v nasprotju z cookieStore
globalno.
Posodobitve JavaScript API
Lahko obrnemo situacijo in najdemo še eno težavo, pri kateri lahko posodobitve mehanizma JavaScript brskalnika prekinejo globalne reference imenovanega elementa.
Na primer:
window.BarcodeDetector.focus();
Ta skript zgrabi sklic na vhodni element in prikliče focus()
na njem. Deluje pravilno. Še vedno pa ne vemo, kako dolgi še naprej bo delovalo.
Vidite, globalna spremenljivka, ki jo uporabljamo za sklicevanje na vhodni element, bo prenehala delovati takoj, ko bodo brskalniki začeli podpirati BarcodeDetector
API. Na tej točki je window.BarcodeDetector
global ne bo več referenca na vhodni element in .focus()
bo vrgel "window.BarcodeDetector.focus
ni funkcija«.
Bonus: vsi poimenovani elementi ne ustvarjajo globalnih referenc
Želite slišati nekaj smešnega? Za dodatno žalitev so poimenovani elementi dostopni kot globalne spremenljivke samo, če imena ne vsebujejo nič drugega kot črko. Brskalniki ne bodo ustvarili globalne reference za element z ID-jem, ki vsebuje posebne znake in številke, na primer hello-world
in item1
.
zaključek
Naj povzamemo, kako smo prišli sem:
- Vsi večji brskalniki samodejno ustvarijo globalne reference na vsak element DOM z
id
(ali v nekaterih primerih aname
atribut). - Dostop do teh elementov prek njihovih globalnih referenc je nezanesljiv in potencialno nevaren. Uporaba
querySelector
orgetElementById
namesto tega. - Ker se globalne reference generirajo samodejno, imajo lahko nekatere stranske učinke na vašo kodo. To je dober razlog, da se izogibate uporabi
id
atribut, razen če ga res potrebujete.
Na koncu dneva je verjetno dobra ideja, da se izogibate uporabi poimenovanih globalov v JavaScriptu. Prej sem citiral specifikacijo o tem, kako to vodi do "krhke" kode, toda tukaj je celotno besedilo, ki pove bistvo:
Splošno pravilo je, da bo zanašanje na to povzročilo krhko kodo. Kateri ID-ji se na koncu preslikajo v ta API, se lahko sčasoma razlikujejo, ko se na primer spletni platformi dodajo nove funkcije. Namesto tega uporabite
document.getElementById()
ordocument.querySelector()
.
Menim, da dejstvo, da sama specifikacija HTML priporoča izogibanje tej funkciji, govori samo zase.