You can interrogate the Array methods by going through the constructor to get the prototype. In our case, lets look at Array.prototype by using Object.getOwnPropertyNames. This will tell us all of the supported Array methods and properties. Its actually quite a large number, and many of them are very helpful.
Most of them are useful for initial iteration, and most of them, once invoked the first time, will return a new array, allowing you to chain operations together. Combining map's, filter's and reduce operations can solve a large set of problems. It would be great if you could use those on HTMLCollection and other DOM types that aren't themselves Array's.
["length", "constructor", "toString", "toLocaleString", "join", "pop", "push", "concat", "reverse", "shift", "unshift", "slice", "splice", "sort", "filter", "forEach", "some", "every", "map", "indexOf", "lastIndexOf", "reduce", "reduceRight", "entries", "keys"]
HTMLCollectionSo what is an HTMLCollection? In the words of Dr Frankenstein, "It's Alive!!!". Unlike most collections that only mutate if you add/remove items from them, the HTMLCollection is a live view of the DOM. For instance, if you call getElementsByTagName("p") to get a collection back, then modify the DOM, your returned collection will already be updated to include the modifications made. No need to query getElementsByTagName again. You can see that here in this fiddle. Note, I also added in StaticNodeList using querySelectorAll to show the differences between a live and static collection. In the case of querySelectorAll, the returned collection does not automatically resolve new DOM mutations and the query must be re-made to get the latest results.
The next bit about HTMLCollection is that it inherits from Object. So by default, it does not get Array methods. It also has its own definition of the length property backed by the Browser itself and in the case of IE and FireFox this is a true ES 5 property, not a field. So it "looks like" an Array but is itself, not an array. If it "looks like" an Array is that good enough?
Executing Array methods on HTMLCollectionFor this step we can use the call, apply or bind methods to redirect any Array method to use our own object as the this pointer. Further, because those methods are duck typed, and simply work with any object that "looks like" an Array, they'll function just as if you had used an actual Array.
So that is pretty neat, and you'll notice some inefficiencies are avoided by this approach. First, the length property is only read at the beginning of the forEach loop. So if you modify the DOM and add more things, you are guaranteed that your loop body will exit anyway since it doesn't fetch length on each iteration. This is similar to lifting the length call out of your for loops, which is common practice among seasoned web developers. Since forEach is a built-in, there may also be JIT time optimizations that would apply that may not apply when using your own for loop (though the reverse is also true so measurement in your scenario can be critical).
Some other common pit-falls are not avoided. For instance, if you mutate the DOM in a way that changes the contents ahead of you in the iteration, then you will see those changes. Including, if someone inserts an element, then you'll process that element, but you will miss an element now beyond your length.
This later issue is easy to avoid. You can turn any HTMLCollection into an Array. The number of ways is staggering, but the easiest might be to invoke the Array constructor passing each element as an argument. But how do you unwrap the collection? You don't need to, it is "like an array" so you can use it with the apply method. (Note: This is in the absence of Array.from which is an ES 6 API)
For Interop and Compat reasons the initial shape of the DOM is sort of fixed to the expectations of the general web. But that shouldn't stop you from molding it to the needs of your application. While removing things from the DOM can cause mashable components to stop working, adding things can often cause them to START working, especially if there is a lack of Interop in that area or if the APIs in question are new. Also, some of the mutations you might make, may already be in scope for the Browser vendors. If you look at classList for instance, most browsers already support this on the Element prototype, so you can imagine any remaining Browser's might follow suit there.