super, self, and sideways & durable vs. template

Brendan Eich brendan at mozilla.com
Sun Jun 12 14:17:42 PDT 2011


On Jun 12, 2011, at 1:00 PM, Peter Michaux wrote:

> Something I try to do when designing constructors is to ensure the
> constructed objects will be durable. That is, if some code modifies or
> breaks parts of the constructed object's exposed interface, the other
> parts of the exposed interface should continue working the same as
> before.

The flip side of "brittle" is "extensible": subclasses might break the base class, or they might usefully extend it. Some cases may need to delegate to the subclass (a la virtual in C++).


> Part of what I use for this is what I term "sideways" calls
> because I don't know any better.

The closure pattern enables this kind of sideways call (not a bad term. I might just say closure call instead of method call, especially if you declare the methods using function definitions instead of assigning to vars.


> My primary reason for asking here is I'm curious if there is any
> allowance for sideways calls in the proposed class syntax. By that I
> mean using the prototype parts of the proposed syntax rather than the
> closure based parts of the syntax (since the syntax can do both.)

Just to be clear to everyone following, you could use the closure pattern as Mark showed, for sure.

But to use the prototypal pattern, you would want something that has been discussed a bit: class elements (class body elements) defining once-per-class-evaluation function, let, and const bindings not as properties, but as lexical bindings in the scope of the prototype methods and accessors.

Currently such Declaration syntax is used to make prototype properties, although methods have a shorthand that obviates the need for "function m1() {...}" as a prototype-property-defining form:

class Monster {
  ...  
  attack(target) {
    log('The monster attacks ' + target);
  }
}

is (IINM, and I could be!) short for:

class Monster {
  ...  
  function attack(target) {
    log('The monster attacks ' + target);
  }
}

I've raised the issue among a couple of people and threads (although possibly not here on es-discuss) of whether the class syntax should rather avoid using declaration syntax to bind prototype properties. Declarations otherwise create lexical bindings.

It seems better to use a variant of the object initialiser extension syntax, although that can veer to far the other way and make a class body resemble a half-way merger of object initiialiser and module body. Say log were a Monster method. Then instead of:

class Monster {
  ...
  log(...rest) { /*...*/}

  attack(target) {
    self.log('The monster attacks ' + target);
  }
}

which as the brittle/extensible bug/feature you cite, you could write

class Monster {
  ...
  function log(...rest) { /*...*/}

  attack(target) {
    log('The monster attacks ' + target);
  }
}

where the function log definition makes a lexical binding in the class body's scope, not visible outside those braces, but statically scoping the attack prototype method.

If you wanted to expose log on the prototype, you'd need:

class Monster {
  ...
  function log(...rest) { /*...*/}

  log = log;

  attack(target) {
    log('The monster attacks ' + target);
  }
}

per the current proposal, which is a queasy mix of assignment expression syntax with prototypal property creation via the extended object initialiser syntax (the attack method, in this case).

Putting a public in front of the assignment helps, slightly:

class Monster {
  ...
  function log(...rest) { /*...*/}

  public log = log;

  attack(target) {
    log('The monster attacks ' + target);
  }
}

The class proposal does allow that much. I don't think going to PropertyAssignment (from the ObjectLiteral grammar) is right, though, since again (and even excluding the local declaration idea exploited by "function log..." in these sketches) the class body is not an object initialiser:

class Monster {
  ...
  function log(...rest) { /*...*/}

  log: log; // yikes -- a bridge too far!

  attack(target) {
    log('The monster attacks ' + target);
  }
}

Each class element ends with ; or } as appropriate, and changing this to comma-separated PropertyAssignments does not pass basic smell tests.

Another avenue: even without a change to enable declarations in class bodies to bind once-per-class lexical bindings in the scope of the prototype methods, another alternative comes to mind: private prototype methods. With private name objects, these could be true properties of the prototype but not nameable outside of the class body's braced extent.

/be


More information about the es-discuss mailing list