[whatwg] Adding ECMAScript 5 array extras to HTMLCollection

On 8/4/10, Jonas Sicking <jonas at sicking.cc> wrote:
> On Wed, Aug 4, 2010 at 11:10 AM, Alex Russell <slightlyoff at google.com>
> wrote:
>> 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:
>>>>>>>

[snip]

To all, please trim quotes to a minimum - thanks.

>>> What would you expect a mutable NodeList to be?
>>
>> A good example would be the result of document.querySelectorAll().
>
> Why couldn't querySelectorAll return a normal Array?
>

It could have done that. It would not be immutable. How much
interoperability issue would changing it now cause? Array could be an
ECMA binding as a potential consideration as an idea for alternative
to non-live static nodelist.

Changing live collections to be Arrays would cause severe
interoperability problems. It would be very complicated.

Then if you're going to get an array, it should be possible to set an
array. I suppose in this sort of situation a setItems method could be
useful:

  var cells = Array.prototype.slice.call(row.cells).sort( comparator );
  row.childNodes.setItems( cells );

The first line is a bit ugly and so it would be nice to have it a more
succinct, not to save typing, per se, but to be clearer:

  var cells = row.cells.toArray().sort( comparator );
  row.childNodes.setItems( cells );

Then again, if ECMA provides top level static Array generics, it's
about the same:

  var cells = Array.slice(row.cells).sort( comparator );
  row.childNodes.setItems( cells );

But that doesn't still address the more fundamental problem of
explaining existing collections. Should they be implemented as native
ES object? Should property access operations ([[Get]]) or
[[HasProperty]] checks be defined more precisely? What are "string
index" and "integer index" with property accessor operators? These
constructs are all used today and without clear definition of what is
to be expected by:

  "submitButton" in form.elements

Existing implementations vary on when they use catchalls. I'd like to
see standardization for this behavior and codification so that
implementations behave similarly -- either use a catchall for a
particular type of collection or use a native object. If a catchall is
used, it would be nice to get "has" checks working with desirable
results.  For example, it would be nice to see - "submitButton" in
elements - result true iff `elements` can [[Get]] a "submitButton"
property.

Garrett

Received on Wednesday, 4 August 2010 15:27:35 UTC