document.scripts 不等于 document.getElementsByTagName("script")

document.scripts is not equal to document.getElementsByTagName("script")

document.scripts returns 一个 HTMLCollection 对象,其中包含文档中所有脚本的列表。同样,document.getElementsByTagName("script") returns 另一个 HTMLCollection 对象保存文档中所有脚本的列表。

我原以为以下陈述是正确的,但事实并非如此。

document.scripts === document.getElementsByTagName("script") // false

这两个不相等的原因是什么?

好吧,我在这里找不到理论推理,但似乎在某些情况下,这些推理可能并不严格等效,实现时必须牢记这一点。例如,在 Chromium 中,实现是不同的,真正有趣的是两者都是 cached 集合:

// renderer/core/dom/document.cc
HTMLCollection* Document::scripts() {
  return EnsureCachedCollection<HTMLCollection>(kDocScripts);
}

// renderer/core/dom/container_node.cc
HTMLCollection* ContainerNode::getElementsByTagName(
    const AtomicString& qualified_name) {
  DCHECK(!qualified_name.IsNull());

  if (GetDocument().IsHTMLDocument()) {
    return EnsureCachedCollection<HTMLTagCollection>(kHTMLTagCollectionType,
                                                     qualified_name);
  }
  return EnsureCachedCollection<TagCollection>(kTagCollectionType,
                                               qualified_name);
}

不只是 scripts:所有文档 HTMLCollection 属性(表单、小程序、图像等)都包含在 EnsureCachedCollection<HTMLCollection>.

然而,对于标签,它是 EnsureCachedCollection<HTMLTagCollection>。虽然 HTMLTagCollection 是 TagCollection 的子项(后者又是 HTMLCollection 的子项),但它们是不同的缓存。

现在按照我期望的缓存工作方式,相同的请求应该给出相同的结果...除非您访问不同的缓存。在这种情况下,相同的结果不仅意味着值相等,而且意味着对象等价。

这就是为什么在对 document.getElementsByTagName('script') 的后续调用和对 document.scripts 的后续调用之间严格等效的原因 - 但不是跨这些调用。

更新:检查@Alohci 的回答,它给出了一个很好的例子,说明这些请求的结果实际上何时会有所不同。

这是一个他们给出不同结果的例子。 document.scripts returns HTMLElement 脚本集,其中 getElementsByTagName("script") returns 所有 scriot 元素集,不考虑命名空间。

(StackSnippets 逻辑向文档添加了两个脚本元素,超出了此处示例代码中显示的元素。)

console.log('There are ' + document.scripts.length + ' document.scripts elements');
console.log('There are ' + document.getElementsByTagName("script").length + ' document.getElementsByTagName("script") elements');
<script>console.log('foo');</script>
<svg><script>console.log('bar');</script></svg>