Specifics of `class` and `super`

# James Long (10 years ago)

First posting to this mailing list. I'm defining a few ES6 features as sweet.js macros so existing projects can easily leverage them. I also happen to think macros are a big deal for JS and this is how future extensions should be defined... (at least ones that are mostly syntactic) My project is here: jlongster/es6-macros

I'm working on class, and it's mostly working. You can see the tests here: jlongster/es6-macros/blob/master/tests/class.sjs, and the generated code here: jlongster/es6-macros/blob/master/tests/class.js

My question is with super. I've searched the archives and read the spec to answer most of my questions, but one thing is unclear. What exactly is the scope of super? Is it valid to use super inside a nested function, like in this example? jlongster/es6-macros/blob/master/tests/class.sjs#L91

If so, super seems somewhat magical in that it can resolve the prototype of a this object that isn't available (when that function is called, this is not the object anymore). Are there clear rules with how super is resolved somewhere?

# Jonathan Barronville (10 years ago)
# Allen Wirfs-Brock (10 years ago)

super is lexically scoped, just like this to the closest enclosing function that defines it. All function definition forms except for arrow functions introduce new this/super bindings so we can just stay that this/super binds according to the closest enclosing non-arrow function definition.

# James Long (10 years ago)

So essentially super is an alias for Object.getPrototypeOf(this) ? I'm trying to interpret the spec but I'm somewhat new to the terms it uses. Not exactly sure "home" is, but I'm sure it's defined elsewhere in there so I'll keep digging into it.

# Allen Wirfs-Brock (10 years ago)

Definitely not the same as Object.getPrototypeOf(this)! see people.mozilla.org/~jorendorff/es6-draft.html#sec-ecmascript-function-objects for definition of [[Home]] see people.mozilla.org/~jorendorff/es6-draft.html#sec-runtime-semantics-definemethod for setting the [[Home]] for a method definition see people.mozilla.org/~jorendorff/es6-draft.html#sec-super-keyword for evaluating the super keyword

# Brendan Eich (10 years ago)

OnMon, Dec 9, 2013 at 10:22 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

super is lexically scoped

This is true.

James Long wrote:

So essentially super is an alias for Object.getPrototypeOf(this) ?

But this is not lexically scoped, so your question's answer must be "no".

# James Long (10 years ago)

I read Allen's email wrong, thought it implied this was lexically scoped (which I know is not true. very little sleep at an airport...).

I'll keep digging through the spec, but if someone has a quick example what ES5 code I could compile to for roughly the same semantics, that would be helpful. From what I understand, you're saying that super is lexically scoped to the class that is defined so I can statically compile it out to something like Foo.prototype.method if Foo is the parent class. Anyway, no need to trail on about this, I should just RTFS.

# Till Schneidereit (10 years ago)

You could also check the output of Traceur and TypeScript. I don't know how close either of them are to implementing the exact semantics of ES6 classes, but I'm sure it'll be helpful in any case.

# Axel Rauschmayer (10 years ago)

super.foo(x) is equivalent to

Object.getPrototypeOf(me.[[HomeObject]]).foo.call(this, x);

(where me refers to the current function)

# Juan Ignacio Dopazo (10 years ago)

On Mon, Dec 9, 2013 at 2:36 PM, Till Schneidereit <till at tillschneidereit.net> wrote:

You could also check the output of Traceur and TypeScript. I don't know how close either of them are to implementing the exact semantics of ES6 classes, but I'm sure it'll be helpful in any case.

Yup. See www.typescriptlang.org/Playground/#src=class Person { private name%3A string%3B constructor(name) { this.name %3D name%3B } say(message%3A string) { return this.name %2B ' says%3A ' %2B message%3B } } class Pirate extends Person { constructor(name) { super('Captn\' ' %2B name)%3B } say(message) { return super.say(message %2B ' Arrr!')%3B } }

# Allen Wirfs-Brock (10 years ago)

On Dec 9, 2013, at 9:28 AM, James Long wrote:

I read Allen's email wrong, thought it implied this was lexically scoped (which I know is not true. very little sleep at an airport...).

I'll keep digging through the spec, but if someone has a quick example what ES5 code I could compile to for roughly the same semantics, that would be helpful. From what I understand, you're saying that super is lexically scoped to the class that is defined so I can statically compile it out to something like Foo.prototype.method if Foo is the parent class. Anyway, no need to trail on about this, I should just RTFS

For an instance method defined in a class definition, [[Home]] is the instance prototype object defined by the class. For a static method defined in a class definition [[Home]] is the class object (ie, the constructor function) itself.

super is not equivalent to a static reference to the super class or super class prototype be super must remain sensitive to prototype chain changes performed using Object.setPrototypeOf()

One way you can approximate [[Home]] in ES5 is to create a __Home property on the function objects that represent methods. Because [[Home]] never changes the __Home property could be non-writable, non-configurable.

At the last TC39 meeting there some decisions made about the use of super outside of class definitions. Those decisions are not yet reflected in the ES6 draft.

# Erik Arvidsson (10 years ago)

TS is known to get this wrong in the name of simplicity.

Traceur gets it right inside class bodies. We do not support toMethod (not even sure if this is doable).

google/traceur-compiler/blob/master/test/feature/Classes/SuperChangeProto.js, goo.gl/0kV4ts

# James Long (10 years ago)

If you have an inner function that calls super, Traceur keeps track of the outer "this" and makes sure to call it with that. TypeScript does not do this. Is this correct?

class Bar extends Foo {
    nestedFunction() {
        function run() {
            return super.getX();
        }

        return run();
    }
}

This code compiles to:

    nestedFunction: function() {
      var $__0 = this;
      function run() {
        return $__superCall($__0, $__proto, "getX", []);
      }
      return run();
    }

where $__superCall basically just does $__proto["getX"].apply($__0, []). But note that it saved the outer this in $__0 and called it with that.

# Erik Arvidsson (10 years ago)

super is lexically bound.

On Mon, Dec 9, 2013 at 7:39 PM, James Long <longster at gmail.com> wrote:

If you have an inner function that calls super, Traceur keeps track of the outer "this" and makes sure to call it with that. TypeScript does not do this. Is this correct?

TypeScript, also does not correctly support super getters/setters:

class B {
  get x() {
    return 1;
  }
}
class C extends B {
  get x() {
    return 1 + super.x;
  }
}

James, it looks like you are using an older version of Traceur which has some bugs related to the [[HomeObject]] starting at the wrong object.