DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Coding
  3. JavaScript
  4. ECMAScript.next: Classes

ECMAScript.next: Classes

Axel Rauschmayer user avatar by
Axel Rauschmayer
·
Aug. 08, 12 · Interview
Like (0)
Save
Tweet
Share
4.97K Views

Join the DZone community and get the full member experience.

Join For Free
During 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:
    1. Constructors are created by classes.
    2. Non-method functions are created by arrow functions [4].
    3. Methods are created by method definitions (inside classes and object literals).
    Each of these syntactic constructs produces functions, but ones that differs slightly from those that are produced by function () {}: #1 functions have properties with different attributes (see below, prototype is frozen, etc.). #2 functions have a bound this. #3 functions have additional data to enable the use of super [2].
  • 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.
    That is the same as for functions. Note that the above is not equivalent to class Foo extends Object, for the only reason that you normally want to avoid Foo inheriting methods such as Object.create().
  • Extend null: class Foo extends null {}
    • The prototype of Foo is Function.prototype.
    • The prototype of Foo.prototype is null.
    Prevent the methods of Object.prototype from being available to instances of Foo.
  • Extend a constructor: class Foo extends SomeClass
    • The prototype of Foo is SomeClass.
    • The prototype of Foo.prototype is SomeClass.prototype.
    Therefore, class methods are inherited, too. For example: If there is a method SomeClass.bar() then that method is also available via Foo.bar(). That is how CoffeeScript implements inheritance [5].
  • Extend a non-constructor: class Foo extends someObject
    • The prototype of Foo is Function.prototype
    • The prototype of Foo.prototype is someObject
Error checks: The extends value must either be an object or null. If it is a constructor then that constructor’s prototype must be either an object or null.

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.
This is exactly like current built-in constructors are set up.

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.
Otherwise, nothing much changes, we still have the same old functions under the hood.

References

  1. ECMAScript: ES.next versus ES 6 versus ES Harmony
  2. A closer look at super-references in JavaScript and ECMAScript.next
  3. Asynchronous programming and continuation-passing style in JavaScript
  4. ECMAScript.next: arrow functions and method definitions
  5. Translating CoffeeScript classes to JavaScript
  6. Prototypes as classes – an introduction to JavaScript inheritance
  7. 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.

Popular on DZone

  • AWS Cloud Migration: Best Practices and Pitfalls to Avoid
  • How to Secure Your CI/CD Pipeline
  • Select ChatGPT From SQL? You Bet!
  • 5 Factors When Selecting a Database

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: