JS: Class Tutorial

By Xah Lee. Date: . Last updated: .

New in ES2015.

class is a keyword, used to create a special function. The class keyword is used in the same way as the keyword function.

The function defined using class is called a class.

The class keyword lets you:

  1. Define a “class” MyClass function that contains a constructor definition C, and in the same class body, a group G of methods.
  2. Automatically creates a object P that that has methods G as properties.
  3. With new MyClass, create a new object X, automatically run the constructor C, and set the object P to be the parent of X. Return X. (if C returns a object, then that object is returned instead.)
  4. With the extend keyword, conveniently create new class function with desired constructor and prototype object.

Note: JavaScript class is not like Java, Python, Ruby's class model. JavaScript's class function is just a convenient function to create objects with specified parent object and methods. No new object model is introduced with the class feature of ES2015. For example, classes in java has a tree structure relationship, and objects created from class mirror that structure. In JavaScript, nothing like that happens. 〔►see JS: Prototype and Inheritance

Class Declaration

Class declaration syntax is class name {}.

Class is a special function object. In many ways, it's same as function.

  1. like function, typeof on class is "function".
  2. like function, parent of class is Function.prototype.
  3. like function, it has properties { length, name, prototype}. (the property name is new in ES2015.)
  4. like function, the value of the property key "prototype", is a new object object with a property key "constructor", by default. 〔►see JS: Property Key "prototype"
  5. like function, the value of this constructor property, is the class function itself.
// class is a special function object, in many ways, it's same as function

// define a empty class named A
class A {}

console.log(
    typeof A
); // function

// parent is Function.prototype
console.log(
    Object.getPrototypeOf (A) === Function.prototype
); // true

// list all properties
console.log( Reflect.ownKeys (A) ); // [ 'length', 'name', 'prototype' ]
// same properties as function would have

// like function, it has a magical property key "prototype"
console.log(
    A.hasOwnProperty ("prototype")
); // true

// like function, the value of this property key "prototype", is a object with property key "constructor"
console.log(
Reflect.ownKeys ( A.prototype )
 );
// [ 'constructor' ]

// the value of the property key "constructor", is A itself
console.log( A.prototype.constructor === A ); // true

Class declaration of the same name cannot happen twice. If so, it's an error.

class x {};
class x {}; // SyntaxError: Identifier 'x' has already been declared

Class Body Syntax

The syntax for class body is:

class class_name { method1 method2 method3 … }

where a method is one of the following form:

Semicolon ; can be used after each method, but is not required.

(Note: class body are all methods. Properties that are not function is not allowed.)

Example:

// example of a class
class A {

    constructor(x) {
        this.p = x;
        console.log("constructor called with arg " + x)
    }

 ff (x) {console.log("ff called with arg " + x)}

 static ss (x) {console.log("ss called with arg " + x)}

 set p1 (x) {console.log("setter called")}

 get p1 () {console.log("getter called")}

}

// ---------------------------
// using the class

// static method is called directly, not via instance of a object
A.ss(3); // prints: ss called with arg 3

// create a instance.
var x = new A(4); // prints: constructor called with arg 4

console.log(x); // A { p: 4 }

x.ff(1); // prints: ff called with arg 1

x.p1 = 1; // prints: "setter called"

x.p1; // prints: "getter called"

Class Semantics

When a class is defined, for example, this:

class MyClass { contructor (constructor_params) {constructor_body} method1 method2 static smethod1 }

A magic property key "prototype" is created. The value of MyClass.prototype is a object object, with all the methods defined in MyClass, except static methods. That is, the value of MyClass.prototype is this:

{ constructor: (constructor_params) {constructor_body}, method1, method2 }

When new MyClass(args) is called:

  1. A new temporary empty object T is created. Parent of T is Object.prototype.
  2. Constructor method is called with arguments args, with its thisBinding having value of T. 〔►see JS: “this” Binding
  3. If the constructor method returns a object, then that object is returned. Done. Else:
  4. Set the value of MyClass.prototype to T's [[prototype]] internal slot. That is, now T'sparent is MyClass.prototype.
  5. T is returned.

The end result is a new object, and its parent is MyClass.prototype, therefore inherit all the non-static methods defined in MyClass.

// The class function defines the prototype object
class C {
    // constructor. called when class is called with new.
    constructor(x) {
        console.log("constructor called with arg " +x);
        this.p = x; // add a property
    }

    ff (x) { console.log("ff called with arg " +x) }

    static ss (x) { console.log("ss called with arg " +x) }

}

// create a object
var cc = new C(3);
// prints: constructor called with arg 3

console.log(cc); // C { p: 3 }

console.log(
Reflect.ownKeys (cc)
) // [ 'p' ]

// show parent
console.log(
Object.getPrototypeOf (cc) === C.prototype
); // true

// show prototype's properties
console.log(
Reflect.ownKeys (C.prototype)
); // [ 'constructor', 'ff' ]

Class Expression

Class can be created by declaration:

class name {…}

Or by expression form:

Class expression returns a value. Can be called inline.

// class is a expression. it returns a value. can be called inline
var x = new (
    class A { constructor(x) { this.p = x; } }
)(3);

console.log(x) // A { p: 3 }

The name in var x = class name {…}; is optional. When given, it allows you to refer to itself inside the class body, but the name is not exposed outsite of it.

// the name of a named class expression is only available inside the class body

var D = class C {
  constructor(x) {
    this.p = x;
  }
};

var e = new C(3);

console.log(e) // ReferenceError: C is not defined

Class expression assigned to a variable the same variable name can happen multiple times. The latter overwrites the previous, just like other variable declared with var or let.

// class declaration cannot happen twice.
// class expression can.
var x = class {};
var x = class {}; // no error
class x {}; // SyntaxError: Identifier 'x' has already been declared

No Name Hoisting

Class name is not hoisted (regardless it's defined via declaration or expression). (That means, the name won't be available until the class declaration is reached. Unlike function declaration. 〔►see JS: Function Declaration vs Function Expression〕 )

// class definition does not have name hoisting
// here's a example with class declaration

var a = new A();
// ReferenceError: A is not defined

class A {}
// class definition does not have name hoisting
// here's a example with class expression

var a = new A();
// TypeError: A is not a constructor

var A = class {};

Class Must be Called with 「new」

Class function must be called with keyword new. It cannot be called like a function by itself.

class A { constructor(x) { this.p = x; } }
var x = A();
// TypeError: Class constructor A cannot be invoked without 'new'

Define Getter/Setter Properties

To define getter/setter properties, just use the get or set keyword in front of the method definition.

〔►see JS: Getter/Setter Properties

class A {
    // getter property
    get m() {
        return new Date();
    }
}

var x = new A;

console.log(x.m); // prints current date time

// (getter properties are properties that return value from a function, but with normal acess syntax not function call)

keyword 「static」

Method names can be declared with a static keyword in front. This is called static method.

Static methods becomes a property of the class function object itself.

Static method does not become part of the prototype object. Thus, object created by the class will not inherit static methods.

// static methods are properties of the class function itself
class D {
    static s(x) { console.log("s called"); return x + 1;}
}

console.log(
    D.s(0)
);
 // prints: s called
// 1

// show all property keys
console.log( Reflect.ownKeys (D) ); // [ 'length', 'name', 'prototype', 's'' ]

Static method cannot be called from a instance of the object. (because static method isn't in the prototype object.)

class E { static f (x) { return x + 1;} }

console.log(
    E.prototype.hasOwnProperty ("f")
); // false

var a = new E;

// static method cannot be called from a instance of the object
console.log(
    a.f(4)
);
// TypeError: a.f is not a function











2017-02-16 following is work in progress

keyword 「extends」

The extend keyword syntax is this:

class B extends A {}

When extend keyword is used, it means:

// understand how extend works

// create a class
class A {}

// extend it
class B extends A {}

// parent of B is A
console.log (
    Object.getPrototypeOf ( B )
); // [Function: A]

// parent of B.prototype is A.prototype
console.log (
    Object.getPrototypeOf ( B.prototype ) === A.prototype
); // true

//------------------------------

// instance of B
var b = new B();

// parent of b is B.prototype
console.log (
    Object.getPrototypeOf ( b ) === B.prototype
 ); // true

Here's a example of extend with added method.

// create a class
class A {
    constructor(x) { this.p  = x; }
    f () { console.log ( "f called" ) }
}

// extend it. adding a method g
class B extends A {
    g () { console.log ( "g called" ) }
}

var b = new B(3);
console.log ( b ); // B { p1: 3 }

b.f(); // prints: f called
b.g(); // prints: g called

// this works because
// parent of b is B.prototype, which contains function g
// parent of B.prototype is A.prototype, which contains function f
// by inheritance, b has access to both g and f

Extend a Class with Constructor

Classes defined by class name {…} is called “base class”.

Classes defined by class name extend name2 {…} is called “derived class” or “extended class”.

Default constructor for base class is constructor() {}

Default constructor for derived class is constructor(…) { super(…); }

keyword 「super」

The keyword “super” is part of JavaScript syntax, it's not a method nor standalone concept. There are 2 syntax with the keyword “super”:

super(…) is a call to the parent's class's constructor. Can only used in the body of constructor definition. When in a derived class, inside a constructor, super(…) must be called. When in a derived class, super(…) must be called before this keyword can be used. (todo can this be used outside constructor?)

super by itself refers parent object. For example, super.method_name refers to parent class's method.

// super.‹method_name› can be used outside constructor body
class A { f (x) {return x + 1;} }
class B extends A { g (x) {return super.f(x);} }
var b = new B;
console.log( b.g(3) ); // 4

ECMAScript 2015 §ECMAScript Language: Expressions#sec-super-keyword

Reference

ECMAScript 2015 §ECMAScript Language: Functions and Classes#sec-class-definitions

ECMAScript 2015 §Annex A#sec-functions-and-classes

ECMAScript 2015 §Ordinary and Exotic Objects Behaviours#sec-makeclassconstructor

ECMAScript 2015 §ECMAScript Language: Functions and Classes#sec-class-definitions

ECMAScript 2015 §Reflection#sec-proxy-constructor

Like what you read? Buy JavaScript in Depth