ES2022 introduces a new feature called static initialization blocks. With static initialization blocks, we can initialize static variables and execute some code only once when the class is loaded.
This article Will discuss what static initialization blocks are, why to use them, and how to use them.
Let's get started.
Before diving into the details of static initialization blocks, Let's understand the static variable first.
Static variables
Static variables are variables that are shared by all instances of a class. They are declared with the static keyword. Static variables are initialized when the class is loaded.
class MyClass {
static staticProp = 'Some value';
}
console.log(MyClass.staticProp)
Let's try to understand the difference between static and instance variables with the help of an example.
class MyClass {
static staticProp = {};
constructor() {
this.instanceProp = {};
}
}
const a = new MyClass();
const b = new MyClass();
console.log(a.staticProp === b.staticProp); // true
console.log(a.instanceProp === b.instanceProp); // false
If we compare the values of the staticProp, we get true as the output. This is because the staticProp is shared by all instances of the class. But if we compare the values of the instanceProp, we get false as the output. This is because the instanceProp is not shared by all instances of the class.
Now we know the difference between static and instance variables. Let's understand the static initialization block.
Static initialization blocks
Static initialization blocks are a special feature of a class that enables more flexible initialization of static properties than can be achieved using per-field initialization. Static blocks allow statements to be evaluated during the initialization of a class.
To define a static initialization block, We can use the static keyword followed by a block({ }). Inside the block, we can initialize static variables and execute some code only once when the class is loaded.
Let's see an example.
class MyClass {
static staticProp;
static {
console.log('Static initialization block');
MyClass.staticProp = {};
}
constructor() {
this.instanceProp = {};
}
}
If we run the above code, we will get the log message 'Static initialization block' in the console and the staticProp will be initialized with an empty object. Our example works like before, but this time we achieved using a static initialization block.
Why use static initialization blocks?
Sometimes we want to initialize static variables conditionally or execute some code only once like database connection, etc. In such cases, we can use static initialization blocks.
Let's assume that we have a function createDBConnection which is responsible for creating a database connection.
const createDBConnection = () => {
console.log('Database connection created');
}
class Connection {
constructor() {
createDBConnection();
}
}
const a = new Connection();
const b = new Connection();
If we create a database connection in the constructor, for every instance of the class, we will create a new connection. which is not a good practice, because 1 class can have multiple instances and we might end up creating thousands of connections.
To solve this problem, we can use static initialization blocks to create a connection, since it will execute only once when the class is loaded.
const createDBConnection = () => {
console.log('Database connection created');
}
class Connection {
static {
createDBConnection();
}
}
const a = new Connection();
const b = new Connection();
If we run the code again, we will get only 1 log message in the console. This way we can get rid of creating multiple connections.
Now we know what static initialization blocks are, Why to use them, and how to use them.
Few more things you should know about static initialization blocks.
A class can have any number of static initialization blocks in its class body, These are evaluated in the order they are declared. Static initialization blocks will be executed from top to bottom.
class Connection {
static {
console.log('Block 1');
}
static {
console.log('Block 2');
}
}
If we run the above code we will get 'Block 1' and 'Block 2' in the console.
They are called before the constructor.
class Connection {
static {
console.log('Block');
}
constructor() {
console.log('Constructor');
}
}
const a = new Connection();
If we run the above code, we will get 'Block' and 'Constructor' in the console.
They are called before the constructor of the derived class. Let's say we have a class BaseConnection and another class Connection that extends BaseConnection and we have a static initialization block in both classes.
class BaseConnection {
static {
console.log('BaseConnection');
}
constructor() {
console.log('BaseConnection constructor');
}
}
class Connection extends BaseConnection {
static {
console.log('Connection');
}
constructor() {
super();
console.log('Connection constructor');
}
}
const a = new Connection();
If we run the above code, the execution order will be
- BaseConnection static initialization block
- Connection static initialization block
- BaseConnection constructor
- Connection constructor
Sometimes it is difficult to understand the execution order of static initialization blocks.
In the same example, If We create an instance of the BaseConnection class instead of the Connection class.
class BaseConnection {
static {
console.log('BaseConnection');
}
constructor() {
console.log('BaseConnection constructor');
}
}
class Connection extends BaseConnection {
static {
console.log('Connection');
}
constructor() {
super();
console.log('Connection constructor');
}
}
const a = new BaseConnection();
If we run the above code, the execution order will be
- BaseConnection static initialization block
- Connection static initialization block
- BaseConnection constructor
As you can see, the static initialization block of the derived class is executed even if we create an instance of the base class. This is because the static initialization block of the derived class is executed before the constructor of the derived class.
Note: Static initialization blocks are executed only once when the class is loaded. No matter whether you are using the class or not, the static initialization block will be executed.
Must Read If you haven't
Maximizing Performance: How to Memoize Async Functions in JavaScript
Rahul Sharma ・ Oct 20 '23
What, Why and How Javascript Static Initialization Blocks?
Rahul Sharma ・ Jan 20 '23
Simplify JavaScript's Async Concepts with One GIF
Rahul Sharma ・ Nov 2 '23
More content at Dev.to.
Catch me on
Youtube Github LinkedIn Medium Stackblitz Hashnode HackerNoon
Top comments (1)
Hi there,
I can't comment on the full article as I haven't read it fully yet, but what you are saying about Static Variables is wrong. I'm just learning about them.
MDN states that static properties are not inherited by instances (the objects created by the class). They're available only in the class itself.
The value of
a.staticProp
andb.staticProp
in your given example is undefined, because the property isn't found in the object nor its prototype. That's whya.staticProp == b.staticProp
.SOURCE