Skip to content

Instantly share code, notes, and snippets.

@zenparsing
Last active August 29, 2015 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zenparsing/5efa4459459b9f04d775 to your computer and use it in GitHub Desktop.
Save zenparsing/5efa4459459b9f04d775 to your computer and use it in GitHub Desktop.
ES Class Initializers

ECMAScript Class Initializer Syntax

Design Goals

  • Minimize changes to the existing ES6 class design.
  • Allow for subclassing built-in classes which are not created by the typical two-phase initialization process.
  • The choice between two-phase and single-phase initialization should be explicitly determined by syntax.

Assumptions

  • Allocation and initialization can't be separated for many built-in classes and host-provided classes.
  • Subclassing built-ins is the exception rather than the rule.

Overview

This design eliminates the special @@create method.

The current class syntax is extended to allow an initializer within the header portion of the constructor. The initializer is an arbitrary expression that, when evaluated, will provide the this value for the constructor when invoked via new. When subclassing a built-in, the user will be responsible for using the initializer to provide a fully-constructed instance of the built-in class. This requirement is applicable to all class descendants of built-in classes.

After the initializer is evaluated, the prototype of the resulting object is set to the class prototype.

All other elements of the design remain the same.

Syntax

ClassConstructor:
    constructor ( StrictFormalParameters ) ClassCreateExpression? { FunctionBody }

ClassCreateExpression:
    extends LeftHandSideExpression

Evaluation Semantics

A constructor C created with class syntax has roughly the following evaluation semantics:

  • Evaluate function parameters (see FunctionDeclarationInstantiation)
  • If the constructor was invoked with [[Construct]]:
    • Assert: The thisValue of the current function environment record is undefined.
    • If ClassCreateExpression exists:
      • Let obj be the result of evaluating ClassCreateExpression using the parameter default evaluation scope.
      • ReturnIfAbrubt(obj)
      • If Type(obj) is not Object, then throw a TypeError exception.
    • Else
      • Let obj be a newly created object.
      • Set the [[Extensible]] internal slot of obj to true.
    • Set the [[Prototype]] internal slot of obj to C.prototype.
    • Set the thisValue of the current function environment record to obj.
  • Evaluate FunctionBody

Note that neither this nor super are allowed within the class initializer expression.

In practice this logic might have to be called from FunctionDeclarationInstantiation and would require internal refactoring.

Example: Subclassing User Classes

class Point {

    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
}

class ColoredPoint extends Point {

    constructor(x, y, color) {
        this.color = color;
        super(x, y);
    }
}

Example: Subclassing User Objects with Initializers

class ColoredPoint extends Point {

    constructor(x, y, color) extends new Point(x, y) {
        this.color = color;
    }
}

Example: Subclassing Array

class Array1 extends Array {

    constructor(x) extends [] {
        this.push(x);
    }
}

class Array2 extends Array1 {

    constructor(x, y) extends new Array1(x) {
        this.push(y);
    }
}

Downsides

This design allows two different techniques for initializing subclasses with the parent constructor. The choice of which to use might not be obvious to the user in some situations. Users will have to develop best practice standards to guide this decision.

Open Questions

  • Would another token work better than extends for introducing the intializer expression?
  • It seems that inside of the initializer expression it would be useful to have some reference to the constructor upon which new was called. Such a feature might be used to call a (possibly overridden) "static" class method. If so, how should that reference be exposed?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment