Several proposals expand the existing JavaScript class syntax with new functionality. This article explains the new public class fields syntax in V8 v7.2 and Chrome 72, as well as the upcoming private class fields syntax.
Here’s a code example that creates an instance of a class named IncreasingCounter
:
const counter = new IncreasingCounter();
counter.value;
counter.increment();
counter.value;
Note that accessing the value
executes some code (i.e., it logs a message) before returning the result. Now ask yourself, how would you implement this class in JavaScript? 🤔
ES2015 class syntax #
Here’s how IncreasingCounter
could be implemented using ES2015 class syntax:
class IncreasingCounter {
constructor() {
this._count = 0;
}
get value() {
console.log('Getting the current value!');
return this._count;
}
increment() {
this._count ;
}
}
The class installs the value
getter and an increment
method on the prototype. More interestingly, the class has a constructor that creates an instance property _count
and sets its default value to 0
. We currently tend to use the underscore prefix to denote that _count
should not be used directly by consumers of the class, but that’s just a convention; it’s not really a “private” property with special semantics enforced by the language.
const counter = new IncreasingCounter();
counter.value;
counter._count;
counter._count = 42;
counter.value;
Public class fields #
The new public class fields syntax allows us to simplify the class definition:
class IncreasingCounter {
_count = 0;
get value() {
console.log('Getting the current value!');
return this._count;
}
increment() {
this._count ;
}
}
The _count
property is now nicely declared at the top of the class. We no longer need a constructor just to define some fields. Neat!
However, the _count
field is still a public property. In this particular example, we want to prevent people from accessing the property directly.
Private class fields #
That’s where private class fields come in. The new private fields syntax is similar to public fields, except you mark the field as being private by using #
. You can think of the #
as being part of the field name:
class IncreasingCounter {
#count = 0;
get value() {
console.log('Getting the current value!');
return this.#count;
}
increment() {
this.#count ;
}
}
Private fields are not accessible outside of the class body:
const counter = new IncreasingCounter();
counter.#count;
counter.#count = 42;
Public and private static properties #
Class fields syntax can be used to create public and private static properties and methods as well:
class FakeMath {
static PI = 22 / 7;
static #totallyRandomNumber = 4;
static #computeRandomNumber() {
return FakeMath.#totallyRandomNumber;
}
static random() {
console.log('I heard you like random numbers…');
return FakeMath.#computeRandomNumber();
}
}
FakeMath.PI;
FakeMath.random();
FakeMath.#totallyRandomNumber;
FakeMath.#computeRandomNumber();
Simpler subclassing #
The benefits of the class fields syntax become even clearer when dealing with subclasses that introduce additional fields. Imagine the following base class Animal
:
class Animal {
constructor(name) {
this.name = name;
}
}
To create a Cat
subclass that introduces an additional instance property, you’d previously have to call super()
to run the constructor of the Animal
base class before creating the property:
class Cat extends Animal {
constructor(name) {
super(name);
this.likesBaths = false;
}
meow() {
console.log('Meow!');
}
}
That’s a lot of boilerplate just to indicate that cats don’t enjoy taking baths. Luckily, the class fields syntax removes the need for the whole constructor, including the awkward super()
call:
class Cat extends Animal {
likesBaths = false;
meow() {
console.log('Meow!');
}
}
Feature support #
Support for public class fields #
Support for private class fields #
Support for private methods and accessors #