Re: What type should .findAll return

On Nov 11, 2011, at 1:05 AM, Jonas Sicking wrote:

> Hi All,
> 
> So, we've debated a lot the exact syntax for .find/.findAll. However I
> intentionally requested that we split out the discussions about return
> type for .findAll to a separate thread. So I'm starting that thread
> here.
> 
> There are a few goals for the return'ed object that I've envisioned
> based on discussions so far:
> 
> 1. It should have at least all of the non-mutating Array methods on
> it. Possibly the mutating methods too if we allow the returned object
> to be mutable.
> 2. It should have a object on the prototype chain where we can insert
> functions that are specifically useful for lists of nodes. Examples
> include .find/.findAll/.matchesSelector/.remove/.addEventListener
> 3. It would be good if it had the Array prototype object on it's
> prototype chain so that if Array.prototype was extended, it would
> affect this object too.
> 4. The object will *not* be live since live results from selector
> matching is slow.
> 
> Since the returned object won't be live, I don't see a reason to make
> it immutable. Hence it seems like we could put Array.prototype on the
> prototype chain which would immediately make all non-mutating as well
> as mutating functions available on the object.
> 
> We should also insert a new prototype object in the prototype chain.
> Hence we end up with something like:
> 
> object -> [some type].prototype -> Array.prototype -> Object.prototype.
> 
> And to ensure that the object acts as much as possible as an array it
> should also have it's [[Class]] set to that of an array.

This is not something to do lightly -- at least cross-post the first message to es-discuss@mozilla.org and set reply-to followups-to. Cc'ing Allen.

Note that [[Class]] is going away in ES.next.

The internal methods and properties of ECMA-262 are not arbitrary extension points for other specs to use without consultation.

/be


> This has
> subtle effects on a number of functions. For example it affects what
> Object.toString() and Array.isArray returns, it affects how
> Array.concat behaves, and it affects the JSON serializer.
> 
> I'm not sure if setting the [[Class]] to that of an array also gives
> the object the magical .length property, but if it doesn't, we need to
> also define that the returned object has such a property. Note that
> for Arrays, .length doesn't live on the prototype chain, but is rather
> a special property on the object itself.
> 
> In other words, the returned object is exactly what you'd get if you did:
> 
> a = new Array;
> a.__proto__ = [some type].prototype;
> [some type].prototype.__proto__ = Array.prototype;
> 
> and then filled a with the set of nodes which matched the selector
> passed to .findAll.
> 
> 
> So the remaining question is, what should we use for [some type]. One
> option is to use NodeList. However this would result in NodeLists
> having Array.prototype on it's prototype chain. Including all mutating
> functions. This is iffy in general since NodeLists are returned from
> several APIs which return objects which represent a live result of a
> query. For example .getElementsByTagName, .getElementsByClassName and
> .childNodes.
> 
> An additional source of iffiness with this idea is that several of the
> mutating methods on Array.prototype don't throw if called on a
> immutable objects. For example .pop, .shift, .sort and .reverse all
> would not throw if called on an empty immutable list.
> 
> Hence I propose that we add a new type. I don't care much for naming
> things so I'll just suggest NodeArray for now and let others fight it
> out over the name.
> 
> For now we can leave NodeArray as empty and just let it be an
> extension point for page authors. We can discuss separately if
> .findAll/.matchesSelector should be added to NodeArray, and if so how
> they should behave.
> 
> However, we should probably use NodeArray to "fix" one of the problems
> with some of the functions on Array.prototype. For example
> Array.prototype.filter always returns a new Array object. This would
> mean that:
> 
> elem.findAll(...).filter(function(node) { ... });
> 
> will return a plain Array and not a NodeArray. However we could make
> NodeArray override all such functions and keep their behavior
> identical except that they return NodeArrays.
> 
> Another way to fix this problem would be to change the definition of
> Array.prototype.filter, but I have no idea if that's doable, or how
> that would be done.
> 
> 
> What do people think?
> 
> / Jonas
> 

Received on Friday, 11 November 2011 18:07:13 UTC