为什么 document.all 是假的?

IT技术 javascript dom
2021-03-01 19:45:25

document.all 是 DOM 中为假的非原始对象。

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

if (document.all) {
    alert("hello");
}

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

4个回答

免责声明: 我是在twitter上发布导致此线程的问题的人:)这是我会在我的前沿趋势演讲中提出和回答的问题我在上台前 5 分钟写了这条推文。


我问的问题如下。

ECMAScript 规范定义ToBoolean()如下

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

如您所见,所有非原始对象(即所有不是布尔值、数字、字符串undefined、 或 的对象null)都是符合规范的。然而,在 DOM 中,有一个例外——一个 DOM 对象是假的。你知道那是哪一个吗?

答案是document.allHTML 规范说:

all属性必须返回一个HTMLAllCollectionDocument节点根的节点,其过滤器匹配所有元素。

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

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

JavaScript中的==and!=运算符而言,用户代理必须表现得好像返回的对象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
}

基本上,很长一段时间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
}

但遗憾的是,许多现有的代码都是反过来的。

解决这个问题的最简单的方法是document.all在仍然模仿它的浏览器中简单地 make be falsy。

@adrian 欢迎来到网络,由于遗留功能,一切都变得复杂:)
2021-04-23 19:45:25
但是在 17 年 10 月,他们删除了此通知,尽管直到今天这种行为仍然保持不变......
2021-05-03 19:45:25
所以我们有mariusschulz.com/blog/... smh
2021-05-11 19:45:25
一个过时的功能的相当复杂的答案。
2021-05-19 19:45:25

ES2019 更新

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

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

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

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


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

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

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

  • 检查它是否在 Internet Explorer 中运行以使用其非标准功能(如document.allActivex)的代码;

    if (document.all) {
        useActiveXStuff();
    }
    
  • 假定它在 Internet Explorer 中运行并使用document.all.

    document.all["my-button"].onclick = function () {
        alert("hi");
    };
    

现代浏览器不再实现这个过时的东西。它是由 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

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

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

简而言之,就是让这两个代码示例都能正常工作。浏览器必须这样做,这样旧的网页才能继续工作。

示例 1

// Internet Explorer
if (document.all) {
    useActiveX()
}
// Netscape Navigator
else {
    useOldButStillWorkingCode()
}

样本 2

document.all.output.innerHTML = 'Hello, world!'