On Nov 30, 2012, at 2:20 PM, Jason Orendorff wrote:

> On Tue, Nov 27, 2012 at 12:40 PM, Allen Wirfs-Brock <al...@wirfs-brock.com> 
> wrote:
> On Nov 27, 2012, at 5:07 AM, David Bruant wrote:
> >>    WeakMap.call(o);
> >>    WeakSet.call(o);
> >>    Map.call(o);
> >>    Set.call(o);
> >>
> >> Currently, this works and makes o a weakmap, a weakset, a map and a set...
> 
> Sort of.  They won't inherit from any of the corresponding prototype types so 
> none of the relevant methods will be available on those objects.  However 
> they could be explicitly invoked on the object.  If you want to make them all 
> available then the individual methods would have to be installed with 
> non-conflicting names.
> 
> But overall, why should the above be a problem?  It is how ES "construction" 
> works and the way things work for any user defined objects.  Why should 
> built-ins be any different?
> 
> I still don't care for this. It adds a level of indirection, people seem to 
> find it surprising, and I don't see the benefit.
> 
> Here's a counterproposal, another way to support subclassing builtins. 
> Suppose we introduce a method, Object.prototype.@@create, that's called when 
> you call Object.create(x) or new Object. It takes no arguments except 'this'; 
> all it does is create a new object with 'this' as the [[Prototype]]:
> 
>     Object.create(x)   <==>   x.@@create()
>     new C(x, y)   <==>   C.call(C.prototype.@@create(), x, y)
> 
> Then add Map.prototype.@@create which creates a Map with empty [[MapData]]. 
> Likewise Set.prototype.@@create and so on.

This is not unreasonable. It is actually pretty great but I think needs a 
little tweaking. Let me try to explain in somewhat different terms and with a 
slight variation.

The ordinary [[Construct]] internal method (which is what the new operator 
actually uses) when invoked upon a constructor function performs  two steps. It 
first instantiates a new object instances (and sets its [[Prototype]] internal 
property).  It then calls the constructor function using the new object as the 
this value so that the constructor can perform initialization.

In the past on this list we have discussed the utility of being able to 
separately over-ride the [[Construct]] and [[Call]] behavior of a function (and 
Proxies enable this).  What Jason is proposing is a variation of this.  Instead 
of exposing a @@method that completely replaces [[Construct]] he is proposing a 
method that replaces the allocation step of [[Construct]].  Such a method, in 
addition to performing actual allocation, could also perform any allocation 
time initialization that should be separate from the constructor functionality. 
This permits the constructor function to be super invoked without doing 
allocation-time initialization upon the subclass instance.

Overall, I like this approach. However, I don't think @@create belongs on the 
prototype object.  This make the @@create functionality for a particular kind 
of object available to anyone who gets their hands on  an instance object of 
the class.  This smells like a capability leak.

Instead, I would make @@create a property of the actual constructor function 
and I would place the default @@create on Function.prototype, which all 
functions inherit from.

So roughly speaking, Foo.[[Constructor]](...args) would be defined as:
1) Let creator be Foo.[[Get]](@@create)
2 ) Let newObj be creator.call(foo).  //Foo is passed as the this value to 
@@create
3)  Let ctorResult be Foo.[[call]](newObj,args)
4)  If Type(ctorResult) is Object, return ctorResult
5) else return newObj

The definition of the Function.prototype.@@create would be loosely

function() {
    return Object.create(this.prototype);
}

So, Map would be defined with a Map.@@create method that in sorta specTalk 
would say something like:
1) Let obj be a new ordinary object.
2) Let proto be this.[[Get]]('Prototype').
2) Set the [[Prototype]] of obj to proto.
3)  Add a [[MapData]] internal property to obj.
4) Return obj

The actual Map constructor function would be specified just like  it is 
currently.  It primarily deals with initialize the new object from arguments 
passed to the constructor.
Then somebody could code:

class MyMap extends Map{
    constructor (...args) {
        super(...args);
        this.myAnswer = 42
    }
}

And then
   new MyMap();
would produce an object that has full map functionality but also has a myAnswer 
property.

so, I'm in!  I should have done this originally.  I didn't because I was 
avoiding tackling the problem of providing separate call and construct entry 
points for constructors at this time.  But it really looks like that is the 
best way to support built-in subclassing.

> 
> I think the reason I don't agree with the "this is how things work for 
> user-defined objects" argument is that user-defined objects, by necessity, 
> build their functionality compositionally out of the stuff the language and 
> host already provide. Map and other builtin types are not like that. They 
> exist specifically because they can't be built from other JS parts. There are 
> already hundreds of such classes in the DOM.

Not all built-ins and DOM objects  have the characteristic that they can't be 
built from JS parts. We should want to reduce that hundreds to a handful.

What's nice about this solution is that applies to both built-in and 
self-hosted objects.

A self-hosted implementation of Set (which might be implemented using a Map) 
could defined its @@create as:

let setDate =  new Symbol(true);  //private name, as of this week @-name have 
gone away and [ ] for symbol keyed property access is back
class Set {
  constructor (args) {
     //use args to populate this object
     //likely uses setData property of a Set instance
   }   
  ...
}
Set[Symbols.create] = function() {     //a
      let obj = super();   //create an ordinary object inheriting from 
Set.prototype
      obj[setData] = new Map();
      return obj
  }


Good job, Jason!

Allen

> 
> -j
> 
> _______________________________________________
> es-discuss mailing list
> es-discuss@mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to