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

Brendan Eich brendan at mozilla.com
Mon Oct 1 15:38:04 PDT 2012


Allen Wirfs-Brock wrote:
> On Oct 1, 2012, at 12:21 PM, Brendan Eich wrote:
> ...
>> The best reason for using Object is the one Mark raised: class (as sugar for constructor/prototype) uses Object.
>>
>> But if you've read the thread this far, my objective is not to enforce one implementation approach or the other. It's to use @@toStringTag and anything like it so there's no observable difference between the two approaches. Classes should be able to do likewise.
>>
>> Otherwise we have
>>
>>   class C extends B {...}
>>
>> and
>>
>>   C.prototype instanceof Object
>>
>> when the cowpath today wants
>>
>>   C.prototype instanceof B
>
> I'm not sure  what you are arguing for/against with the above point about instanceof.

I'm not arguing "for" or "against" yet, just observing that we can't 
rule out the possibility that a class constructor.prototype is *not* a 
plain old Object instance, and (if B has mutable state hidden behind 
accessors) presents the side channel Mark pointed out.

> instanceof is it's own mechanism that is independent of toString,  [[Class]] tagging, or any concept that is used in section 15 of  ES<=5.1 when it is talking about instances of the various built-ins.  About the best you can do is create a table that compares all the various mechanism that do something like an "instance of" check against the actual built-in and look for the consistencies/inconsistencies.

Yes, the toString tagging is separate and can be handled (we should 
decide how it should be handled, though. Is C.prototype.constructor === 
C even though C.prototype instanceof B not C?).

The side channel issue remains.

> The current spec. language for class definitions would produce true for C.prototype instanceof B and false for C.prototype instanceof C.

Right, and this is saner than the builtins which also have !(C.prototype 
instanceof C) but also tag such that TagOf(C.prototype) === "C".

>    But it would also answer true for C.prototype instanceof Object and B.prototype instanceof Object.

One would hope so! :-P

> If there is an inconsistency here, it is that C.prototype claims to be instanceof B but in fact probably hasn't been initialized using the B constructor so it can't actually behave as a B instance.

FWIW, here's what CoffeeScript generates:

(function() {
   var B, C,
     __hasProp = {}.hasOwnProperty,
     __extends = function(child, parent) { for (var key in parent) { if 
(__hasProp.call(parent, key)) child[key] = parent[key]; } function 
ctor() { this.constructor = child; } ctor.prototype = parent.prototype; 
child.prototype = new ctor(); child.__super__ = parent.prototype; return 
child; };

   B = (function() {

     function B() {}

     B.prototype.p = 42;

     return B;

   })();

   C = (function(_super) {

     __extends(C, _super);

     C.prototype.q = 99;

     function C(p, q) {
       this.p = p;
       this.q = q;
     }

     return C;

   })(B);

   console.log(C.prototype instanceof B);

}).call(this);


The __extends function copies class-side heritables but then sets 
child.prototype = new ctor(), so interposes a new Object instance with 
shadowing constructor =  child in between child.prototype and 
parent.prototype.

This is "just" informative, but now that I look at it, it seems to me to 
avoid the mutable state side channel, provided B.prototype is 
(inductively) an Object instance. Neat!

/be




More information about the es-discuss mailing list