The ECMAScript proposal “Class static initialization blocks” by Ron Buckton is at stage 4 and scheduled to be included in ECMAScript 2022.
For setting up an instance of a class, we have two constructs in JavaScript:
For setting up the static part of a class, we only have static fields. The ECMAScript proposal introduces static initialization blocks for classes, which, roughly, are to static classes what constructors are to instances.
When setting up static fields, using external functions often works well:
class Translator {
static translations = {
yes: 'ja',
no: 'nein',
maybe: 'vielleicht',
};
static englishWords = extractEnglish(this.translations);
static germanWords = extractGerman(this.translations);
}
function extractEnglish(translations) {
return Object.keys(translations);
}
function extractGerman(translations) {
return Object.values(translations);
}
Using the external functions extractEnglish()
and extractGerman()
works well ins this case because we can see that they are invoked from inside the class and because they are completely independent of the class.
Things become less elegant if we want to set up two static fields at the same time:
class Translator {
static translations = {
yes: 'ja',
no: 'nein',
maybe: 'vielleicht',
};
static englishWords = [];
static germanWords = [];
static _ = initializeTranslator( // (A)
this.translations, this.englishWords, this.germanWords);
}
function initializeTranslator(translations, englishWords, germanWords) {
for (const [english, german] of Object.entries(translations)) {
englishWords.push(english);
germanWords.push(german);
}
}
This time, there are several issues:
initializeTranslator()
is an extra step that either has to be performed outside the class, after creating it. Or it is performed via a workaround (line A).initializeTranslator()
does not have access to the private data of Translator
.With a proposed static block (line A), we have a more elegant solution.
class Translator {
static translations = {
yes: 'ja',
no: 'nein',
maybe: 'vielleicht',
};
static englishWords = [];
static germanWords = [];
static { // (A)
for (const [english, german] of Object.entries(this.translations)) {
this.englishWords.push(english);
this.germanWords.push(german);
}
}
}
One way of implementing enums in JavaScript is via a superclass Enum
with helper functionality (see the library enumify
for a more powerful implementation of this idea):
class Enum {
static collectStaticFields() {
// Static methods are not enumerable and thus ignored
this.enumKeys = Object.keys(this);
}
}
class ColorEnum extends Enum {
static red = Symbol('red');
static green = Symbol('green');
static blue = Symbol('blue');
static _ = this.collectStaticFields(); // (A)
static logColors() {
for (const enumKey of this.enumKeys) { // (B)
console.log(enumKey);
}
}
}
ColorEnum.logColors();
// Output:
// 'red'
// 'green'
// 'blue'
We need to collect static fields so that we can iterate over the keys of enum entries (line B). This is a final step after creating all static fields. We again use a workaround (line A). A static block would be more elegant.
The specifics of static blocks are relatively logical (compared to the more complicated rules for instance members):
The following code demonstrates those rules:
class SuperClass {
static superField1 = console.log('superField1');
static {
assert.equal(this, SuperClass);
console.log('static block 1 SuperClass');
}
static superField2 = console.log('superField2');
static {
console.log('static block 2 SuperClass');
}
}
class SubClass extends SuperClass {
static subField1 = console.log('subField1');
static {
assert.equal(this, SubClass);
console.log('static block 1 SubClass');
}
static subField2 = console.log('subField2');
static {
console.log('static block 2 SubClass');
}
}
// Output:
// 'superField1'
// 'static block 1 SuperClass'
// 'superField2'
// 'static block 2 SuperClass'
// 'subField1'
// 'static block 1 SubClass'
// 'subField2'
// 'static block 2 SubClass'
This is a tiny feature that doesn’t compete with other features. We can already run static code via fields with the static _ = ...
workaround. Static blocks mean that this workaround isn’t necessary anymore.
Other than that, classes are simply one of many tools in the belt of a JavaScript programmer. Some of us use it, others don’t, and there are many alternatives. Even JavaScript code that uses classes often also uses functions and tends to be lightweight.
Class static blocks are a relatively simple feature that rounds out the static features of classes. Roughly, it is the static version of an instance constructor. Its mainly useful whenever we have to set up more than one static field.