Must built-in prototypes also be valid instances? (Was: Why DataView.prototype object's [[Class]] is "Object"?)

Brendan Eich brendan at mozilla.org
Sat Sep 29 17:17:56 PDT 2012


Allen Wirfs-Brock wrote:
> However, there is a bigger issue here.  You could have asked a similar  question about Map.prototype.  Why isn't Map.prototype a Map instances? Is this a bug or an intentional design decision?
>
> Historically, the prototype objects associated with the 9 named built-in constructors were all defined to be instances of those constructors.  For example, Boolean.prototype is a Boolean instance whose value is false.
>
> In writing the specification for Map, I intentionally deviated from that pattern.

Failing to consult with implementors will just make editing churn. I 
don't remember much discussion on this change, if any -- we did talk 
about the general problem of prototype objects being firstborns of their 
class, and how this makes them sometimes not just basis cases but 
degenerate cases.

However, Map is prototyped in SpiderMonkey and V8. In SpiderMonkey, the 
prototype is a Map, not an Object:

js> Object.prototype.toString.call(Map.prototype).slice(8, -1)
"Map"

but you can't get, has, set, size, or iterate Map.prototype:

js> Map.prototype.get('x')
typein:2:0 TypeError: get method called on incompatible Map
js> Map.prototype.has('x')
typein:3:0 TypeError: has method called on incompatible Map
js> Map.prototype.set('x', 42)
typein:4:0 TypeError: set method called on incompatible Map
js> Map.prototype.size()
typein:5:0 TypeError: size method called on incompatible Map
js> for (var [k, v] of Map.prototype) ;
typein:6:13 TypeError: iterator method called on incompatible Map

The error message is suboptimal but what's going on here is that 
Map.prototype has the SpiderMonkey equivalent of [[Class]] == "Map" (or 
the ES6 equivalent). This is important since all the builtins in ES3 + 
Reality (including RegExp; ES3 deviated there) make the prototype for 
built-in class C be of class C.

Your change requires implementations to provide a different built-in 
class (constructor/prototype) initialization path. That's not desirable 
_per se_. Neither IMHO is the user-facing semantic split among "old" and 
"new" constructors.

There are two separate issues here:

1. Should Map.prototype be an instance (firstborn for its realm) of 
class Map?

2. Should Map.prototype be a key/value store that can be used or abused 
as any other Map could be?

We should not mix these up. SpiderMonkey (and possibly V8, I haven't 
tested) says "yes" to 1 and "no" to 2.

>    I did not specify that Map.prototype is a Map instance.  While it has the methods that are applicable to Map instances it does not the internal state that is necessary for those methods to actually work.  For example, if you try to store a value into Map.prototype by calling its set method, you will get a TypeErrior according to the specification. May.prototype can not be used as a map object.

That doesn't mean Map.prototype should not be classified as a Map per 1 
above.

> Why did I do this?  Because, we are defining a significant number new built-in "classes" and it is going to be increasingly hard to define meaningful instance state for all such prototypes.

Not so, as shown above. It's trivial in SpiderMonkey to give the 
prototype no instance state and check for that. Alternative 
implementation techniques are feasible with different trade-offs.

Cc'ing implementors.

/be



More information about the es-discuss mailing list