您知道吗?带有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中文网其他相关文章!