How JavaScript works: class static blocks + 6 proposed semantics
By: Lawrence Eagles
Tags: JavaScript, Web Development, Software Engineering, ES6, Software Development

This is post # 43 of the series, dedicated to exploring JavaScript and its building components. In the process of identifying and describing the core elements, we also share some rules of thumb we use when building SessionStack, a JavaScript application that needs to be robust and high-performing to help companies optimize the digital experience of their users.

Introduction

Class static blocks are currently a stage 3 proposal of the TC39 proposal process.

Class static blocks provide a new way to perform static initialization in a JavaScript class. They are not meant to replace class fields but augment the use case of class fields declarations.

Class static blocks are not new to programming. They have been in use in languages such as Java and C# that use classical inheritance. In Java, they are called Static Initializers, and in C# they are called Static Constructors.

Since JavaScript uses prototypal inheritance, there was no class in JavaScript, until it was added in ES6.

ES6 classes give us a way to emulate classic class inheritance. Hence features such as class static blocks can now be added to the JavaScript language.

It is important to note that classes in JavaScript still use the prototypal inheritance pattern under the hood. They simply provide syntactic sugar for the prototypal inheritance pattern.

To understand class static blocks, we need to have a good understanding of JavaScript classes. And this requires knowledge of object-oriented — OOP JavaScript concepts such as the prototypal inheritance pattern.

So in this article, we would lay the groundwork by learning about some important OOP JavaScript concepts such as the prototypal inheritance pattern, ES6 classes, and the class fields declaration. Then building on our knowledge, we would learn about class static blocks.

Let’s get started with OOP JavaScript in the next section.

ES6 classes and the prototypal inheritance

The term inheritance refers to the procedure in which one object gets access to the properties and methods of another object.

The general idea of inheritance is implemented differently in different languages. Both classical and prototypal inheritance are two different ways of sharing properties and methods between objects.

Classical inheritance is the most popular method of implementing inheritance. Languages such as Java and C# use this pattern.

JavaScript, however, implements inheritance using the prototypal inheritance pattern.

The prototypal inheritance pattern is much simpler, flexible, and easier to extend. It involves the concept of the prototype.

All objects in JavaScript have a prototype property that references the proto object in memory. And the proto property of an object is the prototype of that object.

Consider the code below:

In the code above, we created the john object using Object.create() a JavaScript method that enables us to create a new object using an existing object as its prototype.

Thus, in our code above, the person object is the prototype of the john object and john.__proto__ is the person object.

Also, a prototype can have its own prototype and when the JavaScript engine fails to find a property or method in an object it would look for it in its prototype and continue its search down the prototype chain. It returns the property or method if it finds it or `undefined` if it is not found.

Consider the code below:

In our code above, by setting john as the prototype of the johnson object, we can access the isHuman property from the johnson object. Even if the isHuman property is in the person object which is the prototype of john . This is simply because the JavaScript engine searches for this property down the prototype chain.

The prototype chain should not be confused with the scope chain although they are similar. The scope chain is about looking for where we have access to a variable while the prototype chain is about where we have access to a property or method amongst a sequence of objects connected via the prototype property.

Also, two objects can share the same prototype and this provides a great way of sharing properties and methods between objects. We will learn more about this in the next sub-section.

CustomType declarations

In ES5 there were no classes. The closest thing to a class is a method referred to as creating a custom type. It involves creating a constructor and assigning methods to the prototype of the constructor as seen below:

In the code above, PersonFn is a constructor that creates a single property called name.

And we share the sayName method with all instances of the PersonFn constructor by adding it to the prototype of the PersonFn constructor.

This is because an instance of a constructor will have the same prototype as the prototype of the constructor. Thus, in our code above, both the malePerson and the femalePerson object have access to the sayName method via prototypal inheritance since they are both instances of the PersonFn constructor.

ES6 class declaration

Consider the code below:

The PersonClass declaration above behaves quite similarly to the PersonFn constructor.

But class declaration gives us a concise syntax and allows us to declare the constructor inside the class using the constructor keyword. So there is no need to define a function as the constructor.

The result is quite the same as with the PersonFn constructor. But why classes?

Despite the similarities between classes and custom type declaration, below are some advantages of using classes:

Classes give us a cleaner and concise syntax All code in a class declaration runs in strict mode automatically and there is no way to opt-out of strict mode inside of a class. Class declarations, unlike function declarations, are not hoisted.

Unlike function constructor, calling a class constructor without the new operator throws an error.

The anatomy of a JavaScript class

As we have seen above, ES6 classes are simply syntactic sugar on top of the existing custom type declarations. In this section, we would take a deeper look at classes.

A JavaScript class is a special function. And just as we have function declaration and function expression we can also have class declaration and class expression.

We have already seen a class declaration in the previous section.

A class expression can be either named or unnamed as seen below:

Consider the code below:

The Class constructor

The class constructor is a special method that initializes the instances of the class. It is the place where we set the initial values of the class fields.

Consider the code below:

In the PersonClass above, the constructor has one parameter called name that is used to set the initial value of the name field. Also, in the example above, the name property is an own property.

Own properties are properties that occur on the instance rather than the prototype.

Class fields and the class field declaration proposal

Class fields are the properties of the class. They are variables that hold information. And they can be:

Instance fields — fields that are present on every created instance of a class.

Static fields: static fields are declared using the static keyword. When we declare a field using the static keyword, the field must be accessed from the class directly. And it cannot be accessed from the instance of the class.

Also, class fields have two levels of accessibility:

Public — this field is accessible anywhere, including outside the class

Private — this field is accessible only within the class declaration

Let’s learn about these in detail.

Public class fields

Consider the code below:

In the code above, the PersonClass constructor initializes an instance field called name . we then accessed name from the class instance — the malePerson class using a property accessor. Thus name is a public field.

Also, note that we have to explicitly hard bind the this variable to prevent loss of context in certain cases.

With the class field declaration proposal, we can declare class fields explicitly rather than implicitly as seen above. This approach makes the class self-documenting and more readable.

Consider the code below:

In the code above, we see that the public instance field — name and method — sayName are accessible from the class instance. However, the public static field — PERSON_ROLE could only be accessed from the class using Person.PERSON_ROLE .

Public static fields can be useful for implementing caches or fixed configurations. Also, they can be used for any data you don’t want to be replicated across instances.

Another advantage of using the pattern above is that instance methods like sayName , can be defined as a class field and assigned using the ES6 => arrow function, which is automatically bound to the defining object.

Consequently, it’s no longer necessary to add bind declarations using bind() as seen in the previous example.

It is important to bind this to prevent loss of context. You can learn all about the this variable in our previous article in this series. Also, you can get a deep knowledge of the bind method by reading our article on call, apply, and bind.

Class fields are public by default, but we can create private fields and methods by using the # hash prefix as seen below:

In the code above, we see that #name is a private class field that we can only access within the class declaration using the #getName private method.

Since the sayName instance method calls the #getName private method — which accesses #name within the class declaration, we get John Doe when we call john.sayName().

Also, we are only able to access the private static field — #PERSON_ROLE by using Person.#PERSON_ROLE within the class declaration.

Note, only the class which defines the private static field can access the field.

JavaScript developers often use the conventions such as _FIELDNAME , weakmaps, closure, or symbols to denote private properties but with private class fields, we can simply use the # prefix.

Class static blocks are intended to provide new use cases not handled by public and private class fields and also to augment their existing use cases. Let’s learn about them in the next section.

Class static blocks

In the previous section, we learned that we can create a static class member by prefixing that member with the static keyword. Also, to create a static block, we prefix a code block {} within a class declaration with the static keyword as seen below:

Why class static blocks

The static private and public fields can only enable us to perform per-field initialization of static members during class definition evaluation. In cases where we need to evaluate a statement such as try … catch during initialization or set multiple static fields from the same value, we would have to write this logic outside the class.

Consider the code below:

The code above throws an error. However, we can refactor it using class static block as seen below:

Without static block

With static block

As seen above, with class static blocks, we can evaluate the try…catch statement within the class declaration.

Also, class static blocks provide privileged access to lexically scoped private fields and methods.

This can be useful in cases where we need to share information between a class with an instance private field and a function in the same scope.

Consider the code below:

In the code above, we were able to share the private instance property of the Person class with the readPrivateData function.

This is interesting because as noted above, a private instance property is only accessible within the body of the class that defines it.

Proposed semantics

Below are the proposed semantics of class static blocks:

A static initialization block creates a new lexical scope that is nested within the lexical scope of the class body, granting privileged access to instance private properties for the class.

A class may have multiple class static blocks.

While class fields are a valid target for decorators, class static blocks cannot be decorated. However, you can decorate the class itself. If you are new to JavaScript decorators, you can learn more about them by reading our previous article on decorators in this series.

When evaluated the this variable within the static initialization block is the constructor object.

variable within the static initialization block is the constructor object. It is a Syntax Error to call super — super() within the static initialization block.

to call — within the static initialization block. It is a Syntax Error to reference arguments from within a static {} initialization block.

Conclusion

Class static blocks are useful additions to the JavaScript programming language. They enable new patterns for code sharing and provide new methods of performing static initialization for JavaScript classes.

As a stage 3 proposal, you can enjoy this feature by using compilers such as babel. And after going through this tutorial you should surely be ready to give it a try.

Every new layer of abstraction to make your code more readable and reusable can potentially lead to more difficulties when it comes to troubleshooting. Since code quality and extensibility are critical to every project, you can make the decision of adding more layers of abstraction and compensate for the risk by adding better monitoring and analytics solutions.

A solution like SessionStack will make the debugging process much more efficient as it allows you to replay customer journeys as videos. This allows you to see how customers interacted with your web app and what happened on their screen when an issue occurred. Combining this visual information with all of the technical details from the browser, device, network, and environment will give you all of the context needed to reproduce the problem and resolve it efficiently.

There is a free trial if you’d like to give SessionStack a try.

SessionStack replaying a session

If you missed the previous chapters of the series, you can find them here: