Look Ma, no "this" (was: ECMAScript Harmony)

Mark S. Miller erights at google.com
Tue Aug 19 17:41:08 PDT 2008


On Wed, Aug 13, 2008 at 7:15 PM, Peter Michaux <petermichaux at gmail.com> wrote:
> On Wed, Aug 13, 2008 at 2:26 PM, Brendan Eich <brendan at mozilla.org> wrote:
>
> [snip]
>
>> We talked about desugaring classes in some detail in Oslo. During
>> these exchanges, we discussed several separable issues, including
>> classes, inheritance, like patterns, and type annotations. I'll avoid
>> writing more here,
>
> Is there more to read elsewhere? I'd like to know concretely what
> "desugaring classes" means.

The main difference from the old "Classes as Sugar" proposal is to
desugar to the objects-as-closure style pioneered by Crock rather than
ES3-classical style of prototypical inheritance + this-binding.


Point as a final root class:

function Point(x, y) {
    const self = Object.create(Point.prototype, {
        toString: {value: Object.freeze(function() ('<' + self.getX()
+ ',' + self.getY() + '>'))},
            enumerable: true},
        getX: {value: Object.freeze(function() x),
            enumerable: true},
        getY: {value: Object.freeze(function() y),
            enumerable: true}
    }, true);
    return self;
}

(Assuming that absent attributes default to false, which I don't think
is currently the case in the ES3.1 draft.)

If we stick with zero inheritance, which seemed attractive at Oslo, we
can skip the part about inheritance below. Otherwise, read on.


<inheritance>


Point as a non-final non-abstract root/mixin class where toString is a
final method:

function PointMixin(self, x, y) {
    Object.defineProperties(self, {
        toString: {value: Object.freeze(function() ('<' + self.getX()
+ ',' + self.getY() + '>'))},
            enumerable: true},
        getX: {value: Object.freeze(function() x),
            enumerable: true, flexible: true},
        getY: {value: Object.freeze(function() y),
            enumerable: true, flexible: true}
    });
}
function Point(x, y) {
    const self = Object.create(Point.prototype); // only for instanceof
    PointMixin(self, x, y);
    return Object.freeze(self);
}
Object.freeze(PointMixin);
Object.freeze(Point.prototype);
Object.freeze(Point);


WobblyPoint as a non-abstract non-final subclass:

function WobblyPointMixin(self, wobble) {
    const super = Object.snapshot(self); // a snapshot is a frozen copy
    Object.defineProperties(self, {
        getX: {value: function() (super.getX() + Math.random()*wobble),
            enumerable: true, flexible: true}
    });
}
function WobblyPoint(x, y, wobble) {
    const self = Object.create(WobblyPoint.prototype); // only for instanceof
    PointMixin(self, x, y);
    WobblyPointMixin(self, wobble);
    return Object.freeze(self);
}
Object.freeze(WobblyPointMixin);
WobblyPoint.prototype = Object.create(Point.prototype, {
    constructor: {value: WobblyPoint}
}, true);
Object.freeze(WobblyPoint);


This gets self-overriding a super-binding correct under single
inheritance and even under linearized multiple inheritance.


</inheritance>


Further, methods auto-bind on extraction, as they did in ES4:

const pt = new WobblyPoint(3, 4, 0.1);
const gx = pt.getX;

gx is a no argument function bound to pt. For ES3-style classical
code, you'd instead have to say "pt.getX.bind(pt)".

Notice that the above code works well *because* it never says "this".
JavaScript's "this" is an incredibly tricky construct. However, the
above technique is impractical today because of the extra allocation
cost -- one closure per method per instance. But as Dan Ingalls says
"you can cheat if you don't get caught." The above desugaring shows
how to define the *semantics* of the class construct. The actual
behavior of the class construct must not be observably different from
some such desugaring. But in a class-aware implementation, it should
of course perform better than you'd expect from this desugaring.

In the Caja project, we are exploring whether this optimization can
even be provided as a source-to-source translation:
<http://google-caja.googlecode.com/svn/trunk/doc/html/cajitaOptimization/index.html>.
It's not yet clear that the idea is practically implementable by this
technique <http://groups.google.com/group/google-caja-discuss/browse_thread/thread/df6c8ea9a1ca1aa3>.
But none of these problems should impede a more directly implemented
optimization.

-- 
 Cheers,
 --MarkM


More information about the Es-discuss mailing list