[whatwg] Adding ECMAScript 5 array extras to HTMLCollection

Jonas Sicking jonas at sicking.cc
Mon Aug 2 14:59:28 PDT 2010


On Fri, Jul 30, 2010 at 2:46 PM, Alex Russell <slightlyoff at google.com> 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?

Hard to say given that none exist yet. If there is an API that returns
a mutable NodeList, why not just return a JS Array instead?

> And shouldn't JS support immutable Arrays?

Again, hard to say given that the need doesn't seem to have come up
yet. But assuming that immutable Arrays would make sense, that raises
a host of questions:

Would isArray for them return true or false?
Would the immutable-Array prototype be in the prototype chain for
mutable Arrays?
Would making a change to the prototype chain for normal Arrays be
considered a backwards incompatible change to ECMAScript?
What is the use case for immutable Arrays?
Is a immutable Array the same thing as a NodeList given that NodeLists
can change their contents, for example when a document is mutated?
How fast could we expect EMCAScript to come to reasonable agreement of
how immutable Arrays work?

These all seem like very important questions, and I don't have the
answer to any of them. I do note that there were 10 years between
ECMAScript 3 and 5.

> 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.
>
>>>  - provide a toArray() method or equivalent?
>>
>> I think this would make sense, though I think it's also worth
>> supplying functions that apply array-like operations directly on the
>> nodelist. To avoid the overhead of copying the data.
>
> Elided if they're *actual* Arrays.

At the most they would be actual immutable Arrays, so a toArray()
method would still make sense, though maybe a better name is
toMutableArray() or some such.

>>>  - do nothing?
>>
>> Given how often this comes up, I think it's worth addressing this.
>>
>>>  - 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.

I'd definitely recommend trying to get TC39 to spend time on this.
However my impression is that the best way to get them to do so is to
have a coherent proposal and get a member of TC39 raise it for
inclusion in Harmony. Such a proposal would likely have to answer the
questions I list above.

/ Jonas



More information about the whatwg mailing list