Classes in JavaScript
In object-oriented programming, a class
is a template for creating objects. JavaScript's class
keyword is how you declare a new class in JavaScript.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
const obj = new Rectangle(3, 5);
obj.height; // 3
obj.width; // 5
// The `instanceof` keyword is how you test whether an object was created
// from a certain class.
obj instanceof Rectangle; // true
({}) instanceof Rectangle; // false
Methods
A method is a function defined in your
class that JavaScript adds to every instance of that class. For example, suppose
you wanted to compute the area of a Rectangle
. You can define an area()
method
as shown below.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
// To define a method named `methodName`, you put `methodName() {}` in
// the class declaration.
area() {
return this.width * this.height;
}
}
const obj = new Rectangle(3, 5);
obj.area(); // 15
In a method, the this
keyword refers to the class instance the method is attached to. In the above example,
this
refers to obj
.
Statics
A static is a a function that is defined on the class itself. In JavaScript, a class is just another variable, so you can call static functions on a class.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
// To define a static named `functionName`, you put
// `static functionName() {}` in the class declaration.
static createSquare(length) {
return new Rectangle(length, length);
}
}
const obj = Rectangle.createSquare(5);
obj.height; // 5
obj.width; // 5
Getters/Setters
An alternative way to define the area of a Rectangle
is using getters. Using a getter,
you can make area
a dynamically computed property of a Rectangle
, rather
than a method.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
// To define a getter named `getterName`, put `get getterName() {}`
// in the class definition. Getters are functions!
get area() {
return this.height * this.width;
}
}
const obj = new Rectangle(3, 5);
obj.area; // 15
You can also define a custom setter, which gets called when you set a property.
For example, suppose you want to be absolutely certain that height
and width
are numbers. You can define a custom setter that throws an exception whenever
someone tries the set height
to a non-numeric value.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
get height() {
return this._height;
}
set height(v) {
assert.ok(typeof v === 'number', 'Height must be a number');
return v;
}
get width() {
return this._width;
}
set width(v) {
assert.ok(typeof v === 'number', 'Width must be a number');
return v;
}
}
const obj = new Rectangle(3, 5);
// Throws 'AssertionError: Height must be a number'
obj.height = 'Not a number';
Inheritance
When a class extends
another class, that means the subclass has all the same statics, methods, getters, and setters as the parent class by default. But then the subclass can define additional
statics, methods, getters, and setters. The subclass can also override the base class's statics, methods, getters, and setters.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
area() {
return this.width * this.height;
}
}
class Square extends Rectangle {
constructor(length) {
// `super` refers to the base class' constructor
super(length, length);
}
// The radius of the inscribed circle
// See: see http://mathworld.wolfram.com/Square.html
inradius() {
return this.height / 2;
}
}
const obj = new Square(5);
obj.area(); // 25, from `Rectangle` base class
obj.inradius(); // 2.5, from `Square` class
obj instanceof Square; // true
obj instanceof Rectangle; // true
Inheritance is Still Prototype-Based
The extends
keyword still uses prototype-based inheritance under the hood. That means you can use
prototype-based patterns in combination with ES6 classes.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
Rectangle.prototype.area = function area() {
return this.width * this.height;
};
const obj = new Rectangle(3, 5);
obj.area(); // 15