您知道嗎?帶有ID的DOM元素可在JavaScript中作為全局變量訪問?這是一個存在已久的功能,但我才第一次深入研究它。
如果您第一次聽說這個功能,請做好準備!只需在HTML中向元素添加ID,我們就可以看到它在實際中的應用:
<div id="cool"></div>
通常,我們會使用querySelector("#cool")
或getElementById("cool")
定義一個新變量來選擇該元素:
var el = querySelector("#cool");
但實際上,我們無需這些繁瑣的操作就能訪問#cool
:
因此,HTML中的任何ID(或name屬性)都可以在JavaScript中使用window[ELEMENT_ID]
訪問。再次強調,這並非“新”功能,但很少見。
正如您可能猜到的那樣,使用命名引用訪問全局作用域並非最佳方案。有些人稱其為“全局作用域污染者”。我們將探討原因,但首先……
這種方法在HTML規範中有所描述,其中將其描述為“Window對象的命名訪問”。
Internet Explorer是第一個實現此功能的瀏覽器。其他瀏覽器也添加了此功能。當時,Gecko是唯一一個不直接在標準模式下支持它的瀏覽器,而是選擇將其作為實驗性功能。人們對是否實現它猶豫不決,但為了瀏覽器的兼容性,它最終得以推進(Gecko甚至試圖說服WebKit將其從標準模式中移除),並最終在Firefox 14中進入標準模式。
可能鮮為人知的一點是,瀏覽器不得不採取一些預防措施(成功程度各不相同),以確保生成的全局變量不會破壞網頁。其中一項措施是……
此功能最有趣的部分可能是命名元素引用不會遮蔽現有的全局變量。因此,如果DOM元素的ID已定義為全局變量,它不會覆蓋現有的變量。例如:
window.foo = "bar";
<div id="foo">I won't override window.foo</div>
console.log(window.foo); // 输出 "bar"
反之亦然:
<div id="foo">I will be overridden :(</div>
window.foo = "bar"; console.log(window.foo); // 输出 "bar"
此行為至關重要,因為它可以消除危險的覆蓋,例如<div id="alert"></div>
,否則它會通過使alert API失效而造成衝突。這種保護技術很可能是您(如果您像我一樣)第一次了解它的原因。
前面我說過,使用命名全局元素作為引用可能並非最佳方案。有很多原因,TJ VanToll在他的博客中對此進行了很好的闡述,我將在此處總結:
<a><code><a></a>
),但某些瀏覽器(即Safari和Firefox)會在控制台中返回ReferenceError。
<div><code><div>實例),瀏覽器應該返回一個包含這些實例數組的HTMLCollection。但是,Firefox只返回第一個實例。再說一次,規範指出我們應該在元素樹中使用一個ID實例。但是這樣做不會阻止頁面工作或任何類似的事情。
<li>
<strong>可能有性能成本? </strong> 我的意思是,瀏覽器必須創建該引用列表並維護它。一些人在StackOverflow線程中運行了測試,其中命名全局變量在一個測試中實際上性能更高,而在最近的測試中性能較低。 </li>
<h3>其他注意事項</h3>
<p>假設我們拋棄了反對使用命名全局變量的批評,並繼續使用它們。一切順利。但是,在您這樣做時,您可能需要考慮一些事情。 </p>
<h4>polyfills</h4>
<p>聽起來可能很極端,但這些類型的全局檢查是polyfills的典型設置要求。查看以下示例,我們使用新的CookieStore API設置cookie,在尚不支持它的瀏覽器中對其進行polyfill:</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false"><div id="cool"></div></pre><div class="contentsignin">登入後複製</div></div><div class="contentsignin">登入後複製</div></div>
<p>這段代碼在Chrome中運行良好,但在Safari中會拋出以下錯誤:</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">var el = querySelector("#cool");</pre><div class="contentsignin">登入後複製</div></div><div class="contentsignin">登入後複製</div></div>
<p>在撰寫本文時,Safari不支持CookieStore API。因此,polyfill不會應用,因為img元素ID會創建一個與cookieStore全局變量衝突的全局變量。 </p>
<h4>JavaScript API更新</h4>
<p>我們可以改變情況,找到另一個問題,即瀏覽器JavaScript引擎的更新可能會破壞命名元素的全局引用。 </p>
<p>例如:</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">window.foo = "bar";</pre><div class="contentsignin">登入後複製</div></div><div class="contentsignin">登入後複製</div></div>
<p>該腳本獲取對輸入元素的引用,並在其上調用focus()。它可以正常工作。但是,我們不知道它還能工作多久。 </p>
<p>您會看到,我們用來引用輸入元素的全局變量一旦瀏覽器開始支持BarcodeDetector API就會停止工作。那時,window.BarcodeDetector全局變量將不再是輸入元素的引用,而.focus()將拋出“window.BarcodeDetector.focus is not a function”錯誤。 </p>
<h3>結論</h3>
<p>讓我們總結一下我們是如何走到這一步的:</p>
<ul>
<li>所有主要瀏覽器都會自動創建對每個具有id(或在某些情況下為name屬性)的DOM元素的全局引用。 </li>
<li>通過其全局引用訪問這些元素是不可靠的,並且可能很危險。請改用<code>querySelector
或getElementById
。 最終,最好避免在JavaScript中使用命名全局變量。我前面引用了規範中關於它會導致“脆弱”代碼的內容,但以下是完整的文本,以強調這一點:
一般來說,依賴於此會導致脆弱的代碼。隨著時間的推移,哪些ID最終映射到此API可能會發生變化,例如,隨著新功能添加到Web平台。不要這樣做,請使用
document.getElementById()
或document.querySelector()
。
我認為HTML規範本身建議避免使用此功能就說明了一切。
以上是命名元素ID可以稱為JavaScript Globals的詳細內容。更多資訊請關注PHP中文網其他相關文章!