Why doesn't nodelist have forEach?

Snakes and Coffee picture Snakes and Coffee · Nov 17, 2012 · Viewed 55.6k times · Source

I was working on a short script to change <abbr> elements' inner text, but found that nodelist does not have a forEach method. I know that nodelist doesn't inherit from Array, but doesn't it seem like forEach would be a useful method to have? Is there a particular implementation issue I am not aware of that prevents adding forEach to nodelist?

Note: I am aware that Dojo and jQuery both have forEach in some form for their nodelists. I cannot use either due to limitations.

Answer

Domenic picture Domenic · Nov 19, 2014

NodeList now has forEach() in all major browsers

See nodeList forEach() on MDN.

Original answer

None of these answers explain why NodeList doesn't inherit from Array, thus allowing it to have forEach and all the rest.

The answer is found on this es-discuss thread. In short, it breaks the web:

The problem was code that incorrectly assumed instanceof to mean that the instance was an Array in combination with Array.prototype.concat.

There was a bug in Google's Closure Library which caused almost all Google's apps to fail due to this. The library was updated as soon as this was found but there might still be code out there that makes the same incorrect assumption in combination with concat.

That is, some code did something like

if (x instanceof Array) {
  otherArray.concat(x);
} else {
  doSomethingElseWith(x);
}

However, concat will treat "real" arrays (not instanceof Array) differently from other objects:

[1, 2, 3].concat([4, 5, 6]) // [1, 2, 3, 4, 5, 6]
[1, 2, 3].concat(4) // [1, 2, 3, 4]

so that means that the above code broke when x was a NodeList, because before it went down the doSomethingElseWith(x) path, whereas afterward it went down the otherArray.concat(x) path, which did something weird since x wasn't a real array.

For some time there was a proposal for an Elements class that was a real subclass of Array, and would be used as "the new NodeList". However, that was removed from the DOM Standard, at least for now, since it wasn't feasible to implement yet for a variety of technical and specification-related reasons.