ECMAScript.next: Classes
Join the DZone community and get the full member experience.
Join For FreeDuring the July 2012 meeting of TC39 [1],
classes have been accepted for ECMAScript.next, the upcoming version of
the JavaScript language standard. This blog post explains how those
classes work. It is based on Allen Wirfs-Brock’s annotated slides.
Overview
An ECMAScript.next class is syntactic sugar for a constructor – a function, to be invoked via new. That is, class declarations and class expressions are simply more convenient syntax for writing functions. The following is an example of a class Person having a subclass Employee:// Supertype class Person { constructor(name) { this.name = name; } describe() { return "Person called "+this.name; } } // Subtype class Employee extends Person { constructor(name, title) { super.constructor(name); this.title = title; } describe() { return super.describe() + " (" + this.title + ")"; } }This is how you use these classes:
> let jane = new Employee("Jane", "CTO"); > jane instanceof Person true > jane instanceof Employee true > jane.describe() Person called Jane (CTO)The classes are roughly equivalent to the following code (super has no simple equivalent [2]):
// Supertype function Person(name) { this.name = name; } Person.prototype.describe = function () { return "Person called "+this.name; }; // Subtype function Employee(name, title) { Person.call(this, name); this.title = title; } Employee.prototype = Object.create(Person.prototype); Employee.prototype.constructor = Employee; Employee.prototype.describe = function () { return Person.prototype.describe.call(this) + " (" + this.title + ")"; };
Details
Grammar
ClassDeclaration: "class" BindingIdentifier ClassTail ClassExpression: "class" BindingIdentifier? ClassTail ClassTail: ClassHeritage? "{" ClassBody? "}" ClassHeritage: "extends" AssignmentExpression ClassBody: ClassElement+ ClassElement: MethodDefinition ";" MethodDefinition: PropName "(" FormalParamList ")" "{" FuncBody "}" "*" PropName "(" FormalParamList ")" "{" FuncBody "}" "get" PropName "(" ")" "{" FuncBody "}" "set" PropName "(" PropSetParamList ")" "{" FuncBody "}"Observations:
- Similar to functions, there are both class declarations and class expressions. Also similarly, the identifier of a class expression is only visible within the expression.
- The value to be extended can be produced by an arbitrary
expression. Which means that you’ll be able to write code such as the
following:
class Foo extends combine(MyMixin, MySuperClass) {}
- A class body can only contain methods, no data properties. Prototypes having data properties is generally considered an anti-pattern, so this just enforces a best practice.
- Semicolons are allowed between methods.
- A method is a generator [3] if its name is prefixed with an asterisk (*). Such a method is translated to a property whose value is a generator function.
Various checks and features
- Error checks: the class name cannot be eval or arguments; duplicate class element names are not allowed; the name constructor can only be used for a normal method, not for a getter, a setter or a generator method.
- Class initialization is not hoisted:
new Bar(); // runtime error class Bar {}
That is usually not a problem, because you can still refer to the class everywhere. You just have to wait until the class definition has been evaluated, before you can use it:function useBar() { new Bar(); } useBar(); // error class Bar {} useBar(); // OK
- Instance methods cannot be used as constructors:
class C { m() {} } new C.prototype.m(); // TypeError
That shows that we are heading towards specialization: Whereas previously, the roles constructor, method and non-method function were all taken on by functions, ECMAScript.next will have a dedicated syntactic construct for each role:- Constructors are created by classes.
- Non-method functions are created by arrow functions [4].
- Methods are created by method definitions (inside classes and object literals).
- If there is no method constructor then the default is:
constructor(...args) { super(...args); }
Extending
Rules for extending:- Don’t extend: class Foo {}
- The prototype of Foo is Function.prototype (as for all functions).
- The prototype of Foo.prototype is Object.prototype.
- Extend null: class Foo extends null {}
- The prototype of Foo is Function.prototype.
- The prototype of Foo.prototype is null.
- Extend a constructor: class Foo extends SomeClass
- The prototype of Foo is SomeClass.
- The prototype of Foo.prototype is SomeClass.prototype.
- Extend a non-constructor: class Foo extends someObject
- The prototype of Foo is Function.prototype
- The prototype of Foo.prototype is someObject
Mutability and configurability
Class declarations create (mutable) let bindings. For a given class Foo:- Foo.prototype is neither writeable, nor configurable, nor enumerable.
- Foo.constructor is writeable and configurable, but not enumerable.
- Foo.prototype.* methods are writable and configurable, but not enumerable. Making them writable allows for dynamic patching.
Conclusion
I used to oppose classes and prefer object exemplars [6]. But given the constraint of not breaking legacy code, I’m now glad that they got accepted for ECMAScript.next. The three main benefits of classes are:- Classes will make it easier for newcomers to get started with JavaScript.
- Classes will make subtyping easier for experienced JavaScript programmers. No more helper APIs such as [7].
- Classes will help make code more portable between frameworks. Currently, many frameworks implement their own inheritance API, which makes it more difficult to reuse code.
References
- ECMAScript: ES.next versus ES 6 versus ES Harmony
- A closer look at super-references in JavaScript and ECMAScript.next
- Asynchronous programming and continuation-passing style in JavaScript
- ECMAScript.next: arrow functions and method definitions
- Translating CoffeeScript classes to JavaScript
- Prototypes as classes – an introduction to JavaScript inheritance
- Lightweight JavaScript inheritance APIs
Prototype
JavaScript
Published at DZone with permission of Axel Rauschmayer, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments