[whatwg] Adding ECMAScript 5 array extras to HTMLCollection

Alex Russell slightlyoff at google.com
Wed Aug 4 11:10:15 PDT 2010

Sorry for the lagged response,

On Fri, Jul 30, 2010 at 2:56 PM, Oliver Hunt <oliver at apple.com> wrote:
> On Jul 30, 2010, at 2:46 PM, Alex Russell wrote:
>> On Fri, Jul 30, 2010 at 4:18 AM, Jonas Sicking <jonas at sicking.cc> wrote:
>>> On Thu, Jul 29, 2010 at 5:45 PM, Ian Hickson <ian at hixie.ch> wrote:
>>>> The e-mails quoted below consist of the salient points of this thread:
>>>> On Fri, 23 Apr 2010, David Bruant wrote:
>>>>> Make that HTMLCollection (and all HTML*Collection, as a consequence of
>>>>> inheritence of HTMLCollection) inherit from the ECMAScript Array
>>>>> prototype. This way, it will make available all Array extra methods
>>>>> (forEach, map, filter...) added in ECMAScript5 (and next versions which
>>>>> should go in the same direction).
>>>>> As far as I know, adding this won't break any existing code. The
>>>>> semantics of a Collection and the way it is used is very close from
>>>>> ECMAScript Arrays. I don't think that the notion of "live object" and
>>>>> ECMAScript Array are incompatible either. Once again, I am talking about
>>>>> ECMAScript binding. I have no intention to touch the HTMLCollection
>>>>> interface or other languages bindings.
>>>> On Sun, 25 Apr 2010, J Z wrote:
>>>>> If HTMLCollection was inheriting from Array, and methods like `forEach`,
>>>>> `map`, etc. were to operate on a live object, there would definitely be
>>>>> undesired consequences. We can see this in, say, Firefox (which allows to
>>>>> set [[Prototype]] of `HTMLCollection` to `Array.prototype`):
>>>>> HTMLCollection.prototype.__proto__ = Array.prototype;
>>>>> document.getElementsByTagName('div').forEach(function(el) {
>>>>>   el.parentNode.removeChild(el); // doesn't work as expected
>>>>> });
>>>>> // turning live collection into static array fixes this
>>>>> Array.slice(document.getElementsByTagName('div')).forEach(function(el) {
>>>>>   el.parentNode.removeChild(el);
>>>>> });
>>>> On Sat, 24 Apr 2010, David Bruant wrote:
>>>>> I think I can take your point as a "pro" more than a "con", because in
>>>>> ES5, right before the definition of each array extra method, a paragraph
>>>>> like the following can be found :
>>>>> "The range of elements processed by forEach is set before the first call
>>>>> to callbackfn. Elements which are appended to the array after the call
>>>>> to forEach begins will not be visited by callbackfn. If existing
>>>>> elements of the array are changed, their value as passed to callback
>>>>> will be the value at the time forEach visits them; elements that are
>>>>> deleted after the call to forEach begins and before being visited are
>>>>> not visited."
>>>>> This point is confirmed by every algorithm where the length is "saved"
>>>>> once for all before the loop and not got from the .length property each
>>>>> time.
>>>> On Mon, 26 Apr 2010, Erik Arvidsson wrote:
>>>>> On Sun, Apr 25, 2010 at 01:07, David Bruant wrote:
>>>>>> Le 25/04/2010 00:39, J Z a écrit :
>>>>>>> I have thought a lot about weirdnesses that people could think about
>>>>>>> like trying to assign a value to the HTMLCollection (divs[14] =
>>>>>>> myOtherDiv), but once again, it wouldn't be more allowed than it
>>>>>>> currently is (I have no idea of what happens today, but if an error
>>>>>>> is thrown in a for-loop, it should throw an error as well in a call
>>>>>>> within a forEach).
>>>>>> How would destructive methods like `push` or `sort` behave? Would
>>>>>> `document.body.childNodes.push(document.createTextNode('foo'))` append
>>>>>> text node to a body element? Or would it be a noop?
>>>>>> That is actually a very good point. It think that the behavior should
>>>>>> be exactly the same as "an equivalent without array methods". (this
>>>>>> point of my proposal would need to be made completly explicit for each
>>>>>> method)
>>>>> One way to solve this could be to split Array into two interfaces. One
>>>>> to be used with immutable array like objects and one to use to mutate
>>>>> objects. Then we could apply the immutable array like interface to
>>>>> NodeList and its related interfaces. The benefit of doing that is that
>>>>> NodeList.prototype.push would be undefined instead of failing when
>>>>> called.
>>>> On Mon, 26 Apr 2010, David Flanagan wrote:
>>>>> Rather that trying to make DOM collections feel like arrays, how about
>>>>> just giving them a toArray() method?  This makes it clear that a
>>>>> collection is not an array, but clearly defines a way to obtain an
>>>>> array.  Clever implementors might even be able to optimize common
>>>>> uses-cases using some kind of copy-on-write strategy so that toArray()
>>>>> doesn't involve memory allocation and copying.
>>>>> Of course, trying to teach programmers when they ought to call toArray()
>>>>> and when it is not necessary is another matter.  Perhaps calling the
>>>>> method snapshot() and focusing on the live vs. static distinction
>>>>> instead of the fake array vs. true array distinction would invite less
>>>>> misuse.
>>>>> Or we can just leave the DOM as it is and get used to calling the
>>>>> equivalent of Prototype's $A() function.
>>>> Before changing something this substantial, I'd like to have implementor
>>>> feedback regarding what the best way to address this is:
>>>>  - somehow make HTMLCollections and NodeLists inherit from Array?
>>>>  - define a bunch of feature on HTMLCollections and NodeLists so that
>>>>   they're like arrays?
>>> I don't think this makes sense given the immutability of NodeLists.
>> Wait...what? Shouldn't some sort of NodeList be mutable? And shouldn't
>> JS support immutable Arrays? We need to fix both of these APIs, and we
>> keep heaping back-pressure on JavaScript's Array without any
>> reasonable resolution because we're not exploring how to make Array
>> subtypes work as we want them to for all the use cases (like this)
>> that we care to express.
> What would you expect a mutable NodeList to be?

A good example would be the result of document.querySelectorAll().

> A lot of the common ones are live and always reflect the true state of the document -- they can change on their own but how would they respond to mutation?

The APIs that generate the stateful representations of order should
obviously not allow arbitrary mutation. That's why we need multiple

> Why do you think you need to put pressure on ES Arrays?

I don't, I think the pressure is already there and we're dealing with
it badly (TypedArray, NodeList, etc.).

> All the Array prototype functions are generic and work on anything array like, some of them don't make sense when applied to immutable types, but the majority Just Work.  It's not the fault of EcmaScript if the browsers made a decision years ago to use a distinct type from Array, and to not make that type pick up the Array prototype.

It's ES's fault for not allowing useful Array subclassing sooner.
We'll get there in Harmony, but we need those fixes to say what we
"mean" here.

>>>>  - something else?
>>> John Resig has been working on something in this area. I don't know if
>>> that's ready to publish yet though.
>> I'd like to see any proposal in this area include NodeLists that are
>> *real* Array subtypes. Anything that's not is a missed opportunity
>> both for this list and for TC39.
> Do you really want NodeList to be an Array subtype, or do you just want NodeLists to not be a special magical type in the first place?  And ocne again how does TC39 help? TC39 doesn't control HTML5.

I think you nailed it: the fact that these things are magical host
types is really just sort of offensive when we consider how much work
goes into toolkits to re-build APIs that should just be subsumed by a
"better" NodeList that's a "regular" Array (for some modified
definition of "regular").


More information about the whatwg mailing list