为什么document.all麻痹?

ano*_*ard 36 javascript dom

document.all 是DOM中的非基本对象,是虚假的.

例如,此代码不执行任何操作:

if (document.all) {
    alert("hello");
}
Run Code Online (Sandbox Code Playgroud)

有人可以解释为什么会这样吗?

Mat*_*ens 87

免责声明: 我是推特导致这个帖子的问题的人:)这是一个我会在我的前趋势谈话中提出并回答的问题.我在上台前5分钟写了推文.


我问的问题如下.

ECMAScript规范定义ToBoolean()如下:

ToBoolean(条件),从我的Front-Trends 2012演讲中滑出

正如您所看到的,所有非基本对象(即所有不是布尔值,数字,字符串undefined或对象的对象null)都符合规范.但是,在DOM中,有一个例外 - 一个虚假的DOM对象.你知道那是哪一个吗?

答案是document.all.HTML规范说:

all属性必须返回以节点为HTMLAllCollection根的属性Document,其过滤器与所有元素匹配.

为所有人返回的对象有几个不寻常的行为:

用户代理必须采取行动,就好像ToBoolean()在JavaScript操作转换为返回的对象allfalse值.

对于 JavaScript 中的==!=运算符,用户代理必须表现为返回的对象all等于该 undefined值.

用户代理必须采取行动,以便typeofJavaScript 中的运算符在'undefined'应用于返回的对象时 返回该字符串all.

这些要求是在撰写本文时故意违反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
}
Run Code Online (Sandbox Code Playgroud)

基本上,很长一段时间document.all以这种方式用于检测旧浏览器.因为document.all首先测试,但是提供这两种属性的更现代的浏览器仍然会在document.all代码路径中结束.在现代浏览器中,我们当然更喜欢使用document.getElementById,但由于大多数浏览器仍然具有document.all(出于其他向后兼容性原因),else如果真的document.all是真的,那么它将永远不会被访问.如果代码的编写方式不同,这不会是一个问题:

if (document.getElementById) {
  // code that uses `document.getElementById`, for “modern” browsers
} else if (document.all) {
  // code that uses `document.all`, for ancient browsers
}
Run Code Online (Sandbox Code Playgroud)

但遗憾的是,很多现有的代码都是相反的.

对这个问题最简单的解决方法是简单地document.all在仍然模仿它的浏览器中使其变得虚假.

  • @adrian欢迎来到Web,由于传统功能,一切都很复杂:) (16认同)
  • 所以我们有 https://mariusschulz.com/blog/nullish-coalescing-the-operator-in-typescript#compiled-output-checking-for-null-and-undefined smh (6认同)
  • 很简单的回答是一个过时的功能. (3认同)
  • 有趣的。我认为这是因为 document.all 从未被 Netscape 实现过。您“必须”使用 document.getElementById。对于 IE,您“必须”使用 document.all。因此,没有“较新”或“较旧”的方法来做到这一点,它们只是竞争浏览器,因此没有特别的理由首先具有 document.getElementById 条件。在某个时刻(可能是 Chrome 出现时?) document.all 再次开始工作,但只是因为你无法再对其进行测试...... (2认同)

D. *_*dal 13

ES2019 更新

现在有一个用于对象的[[IsHTMLDDA]] 内部插槽

[[IsHTMLDDA]] 内部插槽可能存在于实现定义的对象上。具有 [[IsHTMLDDA]] 内部槽的对象undefined在 ToBoolean 和抽象相等比较抽象操作中的行为以及用作typeof运算符的操作数时的行为。

HTML 标准也已更新,为实现HTMLAllCollection接口的对象添加了内部插槽:

实现 HTMLAllCollection 接口的对象是具有附加 [[Call]] 内部方法的旧平台对象,如下一节所述。它们还有一个 [[IsHTMLDDA]] 内部插槽。


这种疯狂的原因在 HTML 标准的这个注释中指定:

这些特殊行为的动机是希望与两类遗留内容兼容:一种使用存在document.all作为检测遗留用户代理的方式,另一种仅支持那些遗留用户代理并使用document.all对象而不测试其存在第一的。

所以基本上标准想要兼容这两种类型的代码:

  • 看起来像[DDA代表“文档点全部”](https://zenn.dev/qnighy/articles/1d96f2c0c662f6#:~:text=%E5%90%8D%E5%89%8D%E3%81%AE %20DDA%20%E3%82%82%20文档%20点%20全部%20%E3%81%AE%E6%84%8F%E5%91%B3%E3%81%A7%E3%81%99%E3 %80%82) (4认同)

Ber*_*rgi 7

现代浏览器不再实现这个过时的东西。它是由 IE 引入的,但其他大多数“填充”它以兼容。

为了document.all在支持 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
Run Code Online (Sandbox Code Playgroud)

在 FF 放弃对它的支持之前,它也表现出如此消息中所述的奇怪行为。您可能会在Mozilla 错误 #412247 中找到更多内部结构

W3C 邮件列表存档中还有一个很长的线程,以http://lists.w3.org/Archives/Public/public-html/2009Jun/0546.html开头


Kon*_*ski 5

简而言之,就是让这些代码示例同时工作.浏览器必须这样做才能使旧网页继续工作.

样品1

// Internet Explorer
if (document.all) {
    useActiveX()
}
// Netscape Navigator
else {
    useOldButStillWorkingCode()
}
Run Code Online (Sandbox Code Playgroud)

样本2

document.all.output.innerHTML = 'Hello, world!'
Run Code Online (Sandbox Code Playgroud)