document.all
是在DOM中的非原始对象,其值为假。
例如,以下代码并没有实际作用:
if (document.all) {
alert("hello");
}
有人能解释一下为什么会这样吗?
document.all
是在DOM中的非原始对象,其值为假。
例如,以下代码并没有实际作用:
if (document.all) {
alert("hello");
}
有人能解释一下为什么会这样吗?
声明: 我是那个发推问这个问题的人 :) 这是我在 Front-Trends 演讲中提出并回答的问题。在上台前五分钟,我写下了那个推文。
我提出的问题如下:
ECMAScript 规范定义了 ToBoolean()
方法如下:
可以看到,所有非原始对象(即不是布尔值、数字、字符串、undefined
或 null
的对象)根据规范都被视为真值。然而,在 DOM 中,有一个例外——一个假值的 DOM 对象。你知道它是哪个吗?
答案是 document.all
。根据HTML 规范:
属性
all
必须返回一个以Document
节点为根的HTMLAllCollection
,该集合包含所有元素。返回的对象有几个不寻常的行为:
用户代理必须像在 JavaScript 中使用
ToBoolean()
运算符将返回的对象转换为false
值一样。用户代理必须像在 JavaScript 中对于
==
和!=
运算符的目的一样,在处理返回的对象时,将其视为等于undefined
值。用户代理必须使 JavaScript 中的
typeof
运算符应用于返回的对象时返回字符串'undefined'
。这些要求是有意违反当时写作的 JavaScript 规范(ECMAScript 第5版)。JavaScript 规范要求
ToBoolean()
运算符将所有对象转换为true
值,并且没有规定对象在某些运算符中充当类似undefined
的角色。这种违反是出于与两类旧内容兼容的愿望:一类使用document.all
的存在来检测旧的用户代理,另一类只支持那些旧的用户代理,并在未先测试其是否存在的情况下使用document.all
对象。
因此,document.all
是唯一一个官方例外符合 ECMAScript 规则。(在 Opera 中,document.attachEvent
等也是假值,但这并没有在任何地方规定。)
上述文本解释了为什么要这样做。但这里有一个非常常见的旧网页示例代码片段,可以进一步说明这一点:
if (document.all) {
// code that uses `document.all`, for ancient browsers
} else if (document.getElementById) {
// code that uses `document.getElementById`, for “modern” browsers
}
基本上,很长一段时间以来,人们通常使用document.all
来检测旧浏览器。然而,由于document.all
在测试时优先级较高,因此即使是那些同时提供这两种属性的现代浏览器,也会最终进入document.all
代码路径。当然,在现代浏览器中,我们更喜欢使用document.getElementById
,但由于大多数浏览器仍然具有document.all
(出于其他向后兼容性的原因),如果document.all
为真值,则else
将永远不会被访问。如果代码编写方式不同,这将不会成为问题:
if (document.getElementById) {
// code that uses `document.getElementById`, for “modern” browsers
} else if (document.all) {
// code that uses `document.all`, for ancient browsers
}
但是不幸的是,许多现有的代码却做了相反的事情。
这个问题的最简单解决方法是在仍然模仿它的浏览器中,将 document.all
设为假值。
现在对象有一个[[IsHTMLDDA]]内部插槽:
实现定义的对象上可能存在[[IsHTMLDDA]]内部插槽。具有[[IsHTMLDDA]]内部插槽的对象在 ToBoolean 和 Abstract Equality Comparison 抽象操作以及用作 typeof 运算符的操作数时的行为类似于 undefined。
HTML标准也已经更新,将该内部插槽添加到实现HTMLAllCollection
接口的对象中:
实现HTMLAllCollection接口的对象是具有另一个[[Call]]内部方法的遗留平台对象,在下面的章节中进行了描述。它们还具有一个[[IsHTMLDDA]]内部插槽。
在HTML标准中,这种疯狂的做法的原因在于以下注释:
这些特殊的行为是由于与两类遗留内容兼容性的需求而产生的:一种使用
document.all
的存在作为一种检测旧用户代理的方式,另一种仅支持这些旧用户代理并在没有先测试其存在性的情况下使用document.all
对象。
因此,基本上标准想要与这两种类型的代码兼容:
检查是否在Internet Explorer中运行以使用其非标准功能的代码,例如document.all
和Activex;
if (document.all) {
useActiveXStuff();
}
假设代码在Internet Explorer中运行,并使用document.all
。
document.all["my-button"].onclick = function () {
alert("hi");
};
// Internet Explorer
if (document.all) {
useActiveX()
}
// Netscape Navigator
else {
useOldButStillWorkingCode()
}
document.all.output.innerHTML = 'Hello, world!'
document.all
来区分IE和NN),同时支持document.all
语法,其他浏览器采用了"奇怪"的实现方式,使typeof document.all
返回undefined。Opera> document.all
// prints the array-like object
Opera> typeof document.all
"undefined"
Opera> Boolean(document.all)
false
document.all
会表现出这样的行为。document.all
来自哪里,为什么现代浏览器至今仍支持它?document.all
最初是在Internet Explorer 4中引入的。它的主要用途是通过ID访问元素,就像这样:var element = document.all[id]
后来,W3C标准化了document.getElementById
作为一种通过ID获取元素的方法。
然而,由于IE多年来拥有最大的市场份额,许多网站仅使用document.all
而没有进行测试。
其中一些网站非常受欢迎,但它们会在除IE之外的浏览器中出现故障。
因此,开始讨论在其他浏览器中添加对document.all
的支持,以便使用document.all
的网站可以在这些浏览器中正常工作。
只是为了给你一些讨论的例子,这里有两个来自bugzilla的讨论:
所以,最终,其他浏览器开始实现 document.all
。
然而,由于网站使用 document.all
来检测 IE,使用类似这样的 if
语句:
if (document.all) {
// Use proprietary Internet Explorer APIs
}
document.all
作为行为类似于未定义的 falsy 对象。document.all
仍然在所有主流浏览器中得到支持。你可能会问为什么?可能是因为他们希望旧网站可以正常工作。document.all
及其历史的两分钟 YouTube 视频,如果你感兴趣,请看看:https://youtu.be/KFasyUpmoss
document.all == null
。为什么?仍然保持这个的意义是什么? - MgSam