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

Well, I wasn't able to find the theoretical reasoning here, but it seems that in some cases those might not be strictly equivalent, and implementations have to bear that in mind. For example, in Chromium the implementations are different, and the real funny fact is that both are cached collections:

// 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);
}

It's not just scripts: all the document HTMLCollection properties (forms, applets, images etc.) are wrapped into EnsureCachedCollection<HTMLCollection>.

For tags, however, it's EnsureCachedCollection<HTMLTagCollection>. While HTMLTagCollection is a child of TagCollection (which in turn is a child of HTMLCollection), those are different caches.

Now the way I expect caches to work, the same requests should give the same result... unless you hit different caches. In this case, same result means not just equality of values, but equivalence of objects.

That's why you get strict equivalence between both subsequent calls to document.getElementsByTagName('script') and subsequent calls to document.scripts - but not across those.

UPDATE: Check @Alohci answer, it gives a good example of when the results will actually be different by value for those requests.


Here's an example where they give different results. document.scripts returns the set of HTMLElement scripts, where getElementsByTagName("script") returns the set of all scriot elements regardless of namespace.

(The StackSnippets logic adds two script elements to the document beyond those shown in the example code here.)

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>