Since 2015, JavaScript has improved immensely.
It’s much more pleasant to use it now than ever.
In this article, we’ll look at how to define classes with JavaScript.
Computed Method Names
We can add methods with computed property names.
For example, we can write:
class Foo {
['foo' + 'Bar']() {}
}
We pass an expression that returns a string or symbol into the square brackets to create a method with the given identifier.
We can also pass in a symbol by writing:
class Foo {
[Symbol.iterator]() {
//...
}
}
Generator Methods
We can add generator methods to a class.
For example, we can write:
class Iterable {
constructor(arr) {
this.arr = arr;
}
*[Symbol.iterator]() {
for (const a of this.arr) {
yield a;
}
}
}
We create an Iterable
class with the constructor
and a method with the Symbol.iterator
method.
This makes our Iterable
instance iterable.
The method is a generator as indicated by the *
symbol.
And we use yield
to return and pause each item.
We can then use it with a for-of loop by writing:
class Iterable {
constructor(arr) {
this.arr = arr;
}
*[Symbol.iterator]() {
for (const a of this.arr) {
yield a;
}
}
}
for (const x of new Iterable(['foo', 'bar', 'baz'])) {
console.log(x);
}
And we get each entry of the array we passed in logged.
Subclassing
We can create subclasses from base classes.
The extends
keyword lets us create subclasses.
For example, we can write:
class Person {
constructor(name) {
this.name = name;
}
toString() {
return `(${this.name})`;
}
}
class Employee extends Person {
constructor(name, title) {
super(name);
this.title = title;
}
toString() {
return `(${super.toString()}, ${this.title})`;
}
}
We created the Employee
class with the extends
keyword to create a subclass of Person
.
In the constructor
of Employee
, we call super
to call the Person
constructor from there.
This is required before we access this
in the subclass.
We set the this.title
property with the title
parameter’s value.
In the toString
method, we call super.toString
to call toString
of the Person
instance.
If we create an instance of Employee
, we can check the class that it’s created from.
For instance, we can write:
const emp = new Employee('jane', 'waitress');
console.log(emp instanceof Person);
console.log(emp instanceof Employee);
They both return true
and as we expect.
Person
is the base class and Employee
is the child class.
The Prototype of a Subclass is the Superclass
The class syntax is just a convenient way to create constructors, so it sticks to the prototypical inheritance model.
The prototype of a subclass is the superclass.
If we have:
class Person {
constructor(name) {
this.name = name;
}
toString() {
return `(${this.name})`;
}
}
class Employee extends Person {
constructor(name, title) {
super(name);
this.title = title;
}
toString() {
return `(${super.toString()}, ${this.title})`;
}
}
Then:
console.log(Object.getPrototypeOf(Employee) === Person)
logs true
.
Static properties are inherited from the base class by the child class.
For example, if we have:
class Foo {
static baz() {
return 'baz';
}
}
class Bar extends Foo {}
Then we can call:
Bar.baz()
We can also call static methods in child classes:
class Foo {
static baz() {
return 'baz';
}
}
class Bar extends Foo {
static baz() {
return `child ${super.baz()}`;
}
}
Conclusion
We can add computed method names, static members, and get parent class members from child classes.