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.
Class Details
There are many details in classes.
The value we extend can be an arbitrary expression.
For example, we can extend a class that’s resulted from calling a function:
class Foo extends combine(Foo, Bar) {}
Checks
The JavaScript interpreter makes some checks when we create our classes.
A class name can’t be eval
or arguments
.
Duplicate class names aren’t allowed.
The name constructor
can be used for a normal method and not getters, setters, or generator methods.
Classes can’t be called as a function.
If we do, a TypeError will be thrown.
Prototype methods can’t be used as constructors.
So we can’t write something like:
class Foo {
bar() {}
}
new Foo.prototype.bar();
If we run that, we’ll get the ‘Uncaught TypeError: Foo.prototype.bar is not a constructor’ error.
Property Descriptors
Static properties of a class Bar
are writable and configurable but not innumerable.
Bar.prototype
isn’t writable, enumerable, or configurable.
Bar.prototype.constructor
isn’t writable or enumerable, but it’s configurable.
Properties of Bar.prototype
are writable and configurable, but it’s not enumerable.
This means we can update many properties dynamically.
Classes have Inner Names
A class has its own name
property to return to its inner name.
Its name can also be used in the class itself.
For example, we can write:
class Foo {
bar() {
console.log(Foo.baz);
}
}
Foo.baz = 1;
We referenced Foo
to get the static property.
This is true even if we assign the class to a variable.
For instance, if we have:
const Bar = class Foo {
bar() {
console.log(Foo.baz);
}
}
Bar.baz = 1;new Bar().bar();
Then the bar
method call still logs 1.
This means we can refer it with the original name internally.
Subclassing
We create subclasses by using the extends
keyword.
For static methods, they’re inherited directly by the subclass.
For instance, if we have:
class Person {
static logName() {
console.log(Person.name)
}
}
class Employee extends Person {}
console.log(Person.logName === Employee.logName)
Then the console log will log true
.
The prototype of a JavaScript class is Function.prototype
.
This means that the JavaScript class is actually a function.
We can see this by writing:
class Person {
static logName() {
console.log(Person.name)
}
}
console.log(Object.getPrototypeOf.call(Object, Person) === Function.prototype)
We call the Object.getPrototypeOf
method to get the prototype of the Person
class.
Then we checked that against the Function.prototype
.
And this logs true
.
So we know classes are functions underneath the hood.
Initializing Instances
Class instances are initialized by writing:
function Person(name) {
this.name = name;
}
in ES5.
In ES6, the base constructor is created with the super
method.
This triggers a parent constructor call.
In ES5, the super constructor is called when we use new
to call the constructor.
It’s invoked with the function call.
Conclusion
When we create classes, JavaScript interpreter does many checks.
Also, static methods are inherited by subclasses, and classes are functions.