> class Fox extends Animal {
>   dig: function() {}
> }
> 
> Fox becomes a constructor function with a `.prototype` that is set to an 
> instance of Animal that has been constructed without calling the Animal() 
> constructor. (The usual temporary-constructor-to-hold-a-prototype two step 
> shuffle). All of the own properties from the RHS are merged into the empty 
> prototype. The only mutation here is of a brand new object.

OK.

> Animal is not a prototype here, it's a class (constructor function) in its 
> own right.

Sure, I just didn't realize you wanted copying. I thought you were doing 
something like

    P.__proto__ = SuperClass.prototype;

But IIUC, you're proposing a semantics where you construct a brand new object P 
whose __proto__ is SuperClass.prototype and then copy all the own-properties of 
the RHS into P. That's fine as far as it goes, but super still doesn't work 
(see below).

> Another thing that this doesn't address is super(). Right now in ES5 and 
> earlier, it's pretty painful to call your superclass's constructor:
> 
> In general, I think your arbitrary-expression-RHS design is incompatible with 
> the super keyword, which needs to be lexically bound to its 
> class-construction site. I'll have to think about it, though.
> 
> super() is a separate (but very much needed) issue -- that should still make 
> it in to JS.next regardless of if a new class syntax does. Likewise, super() 
> calls should not be limited only to objects that have been build with our new 
> classes, they should work with any object that has a prototype (a __proto__).

I wish it were possible, but Allen has convinced me that you can't make super 
work via a purely dynamic definition. It has to be hard-wired to the context in 
which it was created. Let me see if I can make the argument concisely.

Semantics #1: super(...args) ~=~ this.__proto__.METHODNAME.call(this.__proto__, 
...args)

This semantics is just wrong. You want to preserve the same |this| as you move 
up the chain of super calls, so that all this-references get the most derived 
version of all other properties.

Semantics #2: super(...args) ~=~ this.__proto__.METHODNAME.call(this, ...args)

This semantics is just wrong. It correctly preserves the most derived |this|, 
but if it tries to continue going up the super chain, it'll start right back 
from the bottom; you'll get an infinite recursion of the first super object's 
version of the method calling itself over and over again.

Semantics #3: super(...args) ~=~ this.__proto__.METHODNAME.callForSuper(this, 
this.__proto__, ...args)

This semantics introduces a new implicit "where did I leave off in the super 
chain?" argument into the call semantics for every function in the language. 
It's sort of the "correct" dynamic semantics, but it introduces an unacceptable 
cost to the language. JS engine implementors will not accept it.

Semantics #4: super(...args) ~=~ LEXICALLYBOUNDPROTO.call(this, ...args)

This semantics properly goes up the super chain via lexical references only, so 
it avoids the infinite recursion of #2. It doesn't introduce any new cost to 
the call semantics of JS, so it doesn't have the cost of #3.

> Depending on whether you implement it as a reference to the prototype, or a 
> reference to the prototype's implementation of the current function -- an 
> approach that I would prefer -- the general scheme is:
> 
> Take the current object's __proto__ and apply the method of the same name 
> there against the current object. If you know the name of the property, and 
> you have the reference to the prototype, I don't see why this would preclude 
> dynamic definitions. 

I thought the exact same thing when I first thought about super. But sadly, as 
Allen taught me, this is semantics #2, which leads to infinite super-call 
recursion.

Dave

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

Reply via email to