JavaScript - Classes
Often, we need to create many objects of the same kind i.e. users. The syntax for a class is as follows:
class MyClass {
// class methods
constructor() { ... }
method1() { ... }
method2() { ... }
method3() { ... }
...
}What class User {...} construct really does is:
- Creates a function named
User, that becomes the result of the class declaration. The function code is taken from the constructor method (assumed empty if we don’t write such method) - Stores class methods, such as
sayHi, inUser.prototype - We can the use
new MyClass()to create a new object with all the listed methods.
The constructor() method is called automatically by new, this is how we initialise the object.
class User {
constructor(name) {
this.name = name;
}
sayHi() {
console.log("Hi " + this.name);
}
}
// Usage:
let user = new User("John");
user.sayHi();So what happens when the new User() is called?
- A new object is created
- The constructor runs with the given argument and assigns it to
this.name - After
new Userobject is created, when we call its method, which is taken from the prototype, so the object has access to class methods
Technically, we could do the same without creating a class but there are still a few important differences to note:
- A function created by a class has a special internal property,
[[isClassConstructor]]: true. Javascript checks for that property in a variety of places and unlike a regular function, must be called with thenewkeyword - A string representation of a class constructor in most JavaScript engines starts with the “class…”
- Class methods are non-enumerable. A class definition sets enumerable flag to
falsefor all methods in the "prototype" which is handy as when we create afor...inloop, we don't want to loop over the methods - Classes always
use strict. All code inside the class construct is automatically in strict mode
Just like functions, classes can be defined inside another expression, passed around, returned, assigned, etc.
let User = class MyClass {
sayHi() {
console.log(MyClass); // MyClass name is visible only inside the class
}
};
new User().sayHi(); // works, shows MyClass definition
console.log(MyClass); // error, MyClass name isn't visible outside of the classor you can create classes dynamically:
function makeClass(phrase) {
// declare a class and return it
return class {
sayHi() {
console.log(phrase);
}
};
}
// Create a new class
let User = makeClass("Hello");
new User().sayHi(); // HelloClasses may include getters/setters, computed properties, etc. This works by creating getters and setters in User.prototype.
class User {
constructor(name) {
this.name = name;
}
get name() {
return this._name
}
set name(value) {
if (value.length < 4) {
console.log("The name is too short!");
return;
}
this._name = value;
}
}
let usr1 = new User("Hannah");
let usr2 = new User("Jon");class User {
['say' + 'Hi']() {
alert("Hello");
}
}
new User().sayHi();A syntax that allows us to add properties. The difference of class fields is that they are set on individual objects, not User.prototype:
class User {
name = "Hannah";
sayHi() {
console.log(`Hello, ${this.name}!`);
}
}
new User().sayHi();JavaScript has a dynamic this and this depends on the context of the call. When passing object methods as callbacks, for instance to setTimeout, there’s a known problem: losing this. There are two approaches to fixing it:
- Pass a wrapper-function, such as
setTimeout(() => button.click(), 1000). - Bind the method to object, e.g. in the constructor. Functions provide a built-in method
bindthat allows to fixthis.
let user = {
firstName: "Hannah"
};
function func() {
console.log(this.firstName);
}
let funcUser = func.bind(user);
funcUser();The super keyword is used to call corresponding methods of super class. This is one advantage over prototype-based inheritance.
class Vulcan {
constructor(name) {
this.name = name;
}
sayHi() {
console.log(`${this.name} says hi in Vulcan.`);
}
}
class Human extends Vulcan {
sayHi() {
super.sayHi();
console.log(`${this.name} says hi in Human.`)
}
}
let human1 = new Human("Spok");
human1.sayHi();
// Spok says hi in Vulcan.
// Spok says hi in Human.Abstract subclasses or mix-ins are templates for classes. In JavaScript we can only inherit from a single object. There can be only one [[Prototype]] for an object and a class may extend only one other class. A mixin is a class containing methods that can be used by other classes without a need to inherit from it.