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.
Superconstructor Calls
We’ve to call super
before we call anything else.
For example, we can’t write:
class Foo {}
class Bar extends Foo {
constructor(foo) {
this.foo = foo;
super();
this.bar = bar;
}
}
The first line must be the super
call.
Instead, we write:
class Foo {}
class Bar extends Foo {
constructor(foo) {
super();
this.foo = foo;
this.bar = bar;
}
}
Removing the super
call also gives us an error. So we can’t write:
class Foo {}
class Bar extends Foo {
constructor() {}
}
Overriding the Result of a Constructor
We can override the result of a constructor by returning something we want in the constructor
.
For example, we can write:
class Foo {
constructor() {
return {};
}
}
Then when we log:
console.log(new Foo() instanceof Foo);
We get false
returned.
It doesn’t matter whether this
is initialized or not since we’re returning an object instead returning this
implicitly in our constructor.
We don’t have to call super
in the child constructor if we override the result as we did in the example.
Default Constructors for Classes
We don’t need to specify an empty constructor if we don’t put anything in there.
So if we have:
`constructor()` `{}`
we can remove it.
For derived classes, we don’t need to add a constructor just to call the super constructor.
So we don’t have to write:
constructor(...args) {
super(...args);
}
in our code.
Subclassing Built-in Constructors
We can create subclasses of built-in constructors.
For example, we can write:
class SomeError extends Error {}
throw new SomeError('error');
We create a subclass of Error
with the extends
keyword.
Then we can throw it like any other Error
instance.
We can also create subclasses of the Array
constructor.
For example, we can write:
class Stack extends Array {
get first() {
return this[0];
}
}
Then we can create a new Stack
instance and use the available Array
properties:
class Stack extends Array {
get first() {
return this[0];
}
}
const stack = new Stack();
stack.push('foo');
stack.push('bar');
console.log(stack.first);
console.log(stack.length);
We called pusg
to push entries to our Stack
instance.
Then we get the first
and length
properties.
first
is the getter we defined.
And length
is inherited from Array
.
Private Data for Classes
JavaScript classes have no private members.
If we want private data, then we’ve to hide them somewhere else.
Or we can just create public members with a special naming scheme to indicate that they’re private.
We can just add an underscore before the property to indicate that they’re private.
For example, we can write;
class Foo {
constructor() {
this._count = 0;
}
}
we add the this._count
instance property to indicate that count
is private.
We can also store private properties with weak maps and symbols.
For instance, we can write:
const _count = new WeakMap();
class Counter {
constructor(count) {
_count.set(this, count);
}
increment() {
let count = _count.get(this);
count++;
_count.set(this, count);
}
}
We create 2 weak maps and use this
as the key for both weak maps.
The values are set to what we pass into the constructor.
Then we can get the value with the weak map’s get
method.
And set the value with the set
method.
Weak maps are useful since we can access the values with this
, preventing the items inside from being accessed any other way.
Conclusion
There are several things we’ve to look at when we call super
.
Also, there are no easy ways to keep variables private.