classes
What I learned, what I’m still figuring out, and where I might go next.
classes
Classes in JavaScript are a template for creating objects. As we learned, unlike many other languages, it’s easy to create JavaScript objects without classes, but that doesn’t mean classes aren’t useful.
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
const user = new User("Lane", 100);
- The
classdeclaration creates a new class - The
constructormethod is a special method that’s called when a new instance of the class (object) is created - The
newkeyword calls the constructor method and creates a new instance of the class
Private Properties
By default, all properties of a class are public, meaning they can be accessed and modified from outside the class. Here’s an example:
class Movie {
constructor(title, rating) {
this.title = title;
this.rating = rating;
}
}
const matrixMovie = new Movie("The Matrix", 9.5);
console.log(matrixMovie.title);
// The Matrix
matrixMovie.title = "The Matrix Reloaded";
console.log(matrixMovie.title);
// The Matrix Reloaded
Maybe we don’t want our title to be able to be changed anywhere in our code. We can make it private by prefixing it with a hash # and declaring it at the top of the class:
class Movie {
#title;
constructor(title, rating) {
this.#title = title;
this.rating = rating;
}
}
const matrixMovie = new Movie("The Matrix", 9.5);
console.log(matrixMovie.#title);
// Uncaught SyntaxError: Private field '#title' must be declared in an enclosing class
Private properties can still be used from within the class:
class Movie {
#title;
constructor(title, rating) {
this.#title = title;
this.rating = rating;
}
getTitleAllCaps() {
const allCaps = this.#title.toUpperCase();
return allCaps;
}
}
const matrixMovie = new Movie("The Matrix", 9.5);
console.log(matrixMovie.getTitleAllCaps());
// THE MATRIX
Static Methods
A static method or property is bound to the class itself, not the instance of the class (an object). In this example, we create two instances of the User class:
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
const lane = new User("Lane", 30);
const allan = new User("Allan", 30);
console.log(lane.name); // Lane
console.log(allan.name); // Allan
In JavaScript, a class is just an object template, so when we create a static method or property the object instances can’t access it. So, static members are often used for utility functions for the class itself.
class User {
static numUsers = 0;
constructor(name, age) {
this.name = name;
this.age = age;
User.numUsers++;
}
static getNumUsers() {
return User.numUsers;
}
}
const lane = new User("Lane", 30);
console.log(User.getNumUsers()); // 1
const allan = new User("Allan", 30);
console.log(User.getNumUsers()); // 2
// This doesn't work because its not a method on the object
console.log(lane.getNumUsers());
// TypeError: lane.getNumUsers is not a function
// at main.js:20:18
Getters and Setters
In JavaScript classes, getters and setters let us define special methods for getting and setting the values of properties. They look like regular methods but are accessed like properties. Here’s an example using the get keyword:
class User {
constructor(name, age) {
this._name = name;
this.age = age;
}
get name() {
return this._name.toUpperCase();
}
}
const lane = new User("Lane", 30);
console.log(lane.name); // LANE
Notice that we’ve renamed this.name to this._name in our constructor to avoid a name collision with the getter itself.
A setter lets us control what happens when a property is changed. For example, we could validate a user’s age to make sure it’s not negative:
class User {
constructor(name, age) {
this.name = name;
this._age = age;
}
get age() {
return this._age;
}
set age(value) {
if (value < 0) {
throw new Error("Age can't be negative.");
}
this._age = value;
}
}
const lane = new User("Lane", 29);
lane.age = -5; // "Age can't be negative."
console.log(lane.age); // 29
Super
The super keyword allows us to call methods on an object’s parent. It’s often used to call a parent’s constructor method when the child object has its own.
class Titan {
constructor(name) {
this.name = name;
}
toString() {
return `Titan - Name: ${this.name}`;
}
}
class BeastTitan extends Titan {
constructor(name, power) {
// call the parent's constructor
super(name);
this.power = power;
}
toString() {
// call the parent's `toString` method
return `${super.toString()}, Power: ${this.power}`;
}
}
const beast = new BeastTitan("Zeke", 9000);
console.log(beast.toString());
// Titan - Name: Zeke, Power: 9000