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

Allen Wirfs-Brock allen at wirfs-brock.com
Tue Oct 2 10:12:59 PDT 2012


On Oct 1, 2012, at 10:37 PM, Brendan Eich wrote:

> Brendan Eich wrote:
>> But if we have a solid branding mechanism (like Domado's ideal in latest browsers? ;-) then that should be used universally and this becomes a don't-care. 
> 
> Just to be crystal clear:
> 
> * in pre-ES6 browsers, no @@toStringTag in the language to hack around with. 
> 
> * in ES6+ browsers, the better branding mechanism and @toStringTag (one @) as public symbol, no worries.
> 
> Yes, this makes a fork in JS code that wants to do tag testing. Old code must use O_p_toString_call (original value of, safe call binding) and string compare. New code wants the better and universal scheme.
> 
> /be
> 

Yes, exactly what I envision.  New code that needs to do branding should do it in a new way.  But I still think we need to protect the integrity of the old mechanism in the presence of @@toStringTag.

It is also probably worthwhile taking a look at how internal branding is done in the draft spec. for Map.

We also need to be careful about making a branding mechanism too prominent.  If we do, too many people will misuse it as a poor man's nominal type system when they really should be doing behavior typing (or even more likely no explicit type checking at all).  Past experience is that excessive class brand checking is an anti-pattern for dynamic OO languages.

I think we have all the language features need to do reliable branding by ES programmers where they need it.  We just need to establish the patterns for doing that. Here is the one I propose:

private @FooBrand;
class Foo {
   constructor() {
        /* establish the internal Fooness of the instance */
        this. at FooBrand = true;
   }
}
Foo.isFoo = function (obj) {return !!obj. at FooBrand};


private @BarBrand;
class Bar extends Foo {
   constructor() {
        super();
        /* establish the internal Barness of the instance */
        this. at BarBrand = true;
   }
}
Bar.isBar = function (obj) {return !!obj. at BarBrand};

Note that an instance of Bar will be true for both Foo.isFoo and Bar.isBar

This  pattern is fine as long as it is ok that anything processed by the Foo or Bar constructor gets branded because not, anybody can do:
   let myFoo = Foo.call({ });

If you really need to strongly tie instantiation with branding you probably have to use a factory function:

module Fooishness {
   export function FooFactory ( ){return  new Foo};
   FooFactory.isFoo = function (obj) {return !!obj. at FooBrand};

   private @FooBrand;
   class Foo {
      constructor() {
           /* establish the internal Fooness of the instance */
           this. at FooBrand = true;
      }
   }
}

Allen






More information about the es-discuss mailing list