ES6 Class

class is a new way to create objects and implement prototypical inheritance, just syntax sugar over prototypical inheritance, let me compare them for you:

Before ES6, define constructor and prototype member:

1
2
3
4
5
6
7
8
9
10
11
12
13
function Circle(radius) {
this.radius = radius;

this.move = function () {
console.log("move");
};
}

Circle.prototype.draw = function () {
console.log("draw");
};

const c = new Circle(1);

After ES6, use one class to do both at the same time:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Circle {
constructor(radius) {
this.raidus = radius;
this.move = function () {
console.log("move");
};
}

draw() {
console.log("draw");
}
}

const c = new Circle(1); // new can not be dropped when using the class syntax

We define members in the body of the class, constructor in a special method used to initialize objects.

All methods defined in the body of the class will be on the prototype of created objects, just like constructor and draw here.

head over to Bable to compile your modern js code into the old fashion code to see their differences.

Hoisting

Unlike function declarations, class declarations will not be hoisted, so will class expression, and BTW, alomost everyone uses class declaration only.

Static Methods

Static methods are only available on the class itself instead of on the object instances, we use static methods to create utility functions that are not specific to a given object, such as:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Circle {
constructor(radius) {
this.radius = radius;
this.move = function () {
console.log("move");
};
}

// instance method
draw() {}

// static method
static parse(json) {
const radius = JSON.parse(json).radius;
return new Circle(radius);
}
}

The static method parse a json string to a circle object, just like JSON.parse() is a utility function which is not meaningful on a particular JSON object. So do the built-in object Math.

The above class with a static method before ES6:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var Circle = (function () {
function Circle(radius) {
this.radius = radius;

this.move = function () {
console.log("move");
};
}

Circle.prototype.draw = function draw() {};

Circle.parse = function parse(json) {
var radius = JSON.parse(json).radius;
return new Circle(radius);
};

return Circle;
})();

The this Keyword

JavaScript has a strict mode, when enabled, JavaScript engine will be more sensitive, it will do more error checking, to enable it, adding the sting on the top of the code:

1
"use strict";

In the strict mode, when we call a method as a function, this will be set to undefined instead of global object, the reason for this is to prevent us from accidently modify the global object.

And note that by default, the body of class is executed in the strict mode:

1
2
3
4
5
6
7
8
9
10
11
class Circle {
sayThis() {
console.log(this);
}
}

const c = new Circle();
c.sayThis(); // the c object

const sayThis = c.sayThis;
sayThis(); // undefined

Private Members Using Symbols

How to make members private in JavaScript?

The stupidest way:

Using an underscore as a naming convention:

1
2
3
4
5
class Circle {
constructor(radius) {
this._radius = radius;
}
}

The Symbol way:

Symbol is a new primitive type introduced in ES6, we can create a symbol by calling Symbol() without new.

1
const _radius = Symbol();

Every time we call Symbol(), we get a new unique value, and we can use that unique value as the property name of an object. So after ES6, when creating a property’s key, not only string is allowed, we can also use a Symbol.

Getter and Setter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const _radius = new WeakMap();

class Circle {
constructor(radius) {
_radius.set(this, radius);
}

get radius() {
return _radius.get(this);
}

set radius(value) {
if (value <= 0)
throw new Error("Invalid radius, radius should be greater than 0");
_radius.set(this, value);
}
}

Inheritance and Method Overriding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Shape {
constructor(color) {
this.color = color;
}

move() {
console.log("move");
}
}

class Circle extends Shape {
constructor(color, radius) {
super(color);
this.radius = radius;
}

draw() {
console.log("draw");
}

move() {
console.log("circle");
super.move();
}
}

Instead of resetting the prototype and constructor, simply use the extends keyword.

To extends before ES6:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function inherits(Child, Parent) {
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
}

var Shape = (function () {
function Shape(color) {
this.color = color;
}

Shape.prototype.move = function move() {
console.log("move");
};

return Shape;
})();

var Circle = (function (Shape) {
inherits(Circle, Shape);

function Circle(color, radius) {
Shape.call(this, color);
this.radius = radius;
}

const _proto = Circle.prototype;

_proto.draw = function draw() {
console.log("draw");
};

_proto.move = function move() {
console.log("circle");

Shape.prototype.move.call(this);
};

return Circle;
})(Shape);