Understanding static class features in JavaScript (ECMAScript 2022)


Static class features

Static fields and methods are not used on instances of a class. Instead, they are called on the class itself and are declared using the static keyword.

Static methods are often utility functions, such as functions to create or clone objects, whereas static properties are useful for caches, fixed-configuration, or any other data we don’t need to be replicated across instances.

In ES2015, static public methods have been introduced. ES2022 adds the remaining static fields and methods:

  • Static public fields
  • Static private fields
  • Static private methods

Let’s talk about them in brief.

Static public fields

Static public fields are added to the class constructor at the time of class evaluation using Object.defineProperty().

class Shape {
  static color = 'blue';

  static getColor() {
    return this.color;
  }

  getMessage() {
    return `Shape is of ${this.color} color` ;
  }
}

The static fields and methods can be accessed from the class itself.

  console.log(Shape.color); // blue
  console.log(Shape.getColor()); // blue
  console.log('color' in Shape); // true
  console.log('getColor' in Shape); // true
  console.log('getMessage' in Shape); // false

Instances cannot access static fields and methods.

  const shapeInstance = new Shape();
  console.log(shapeInstance.color); // undefined
  console.log(shapeInstance.getColor); // undefined
  console.log(shapeInstance.getMessage());//Shape is of undefined color

Static fields can only be accessed by static methods.

  console.log(Shape.getColor()); // blue
  console.log(Shape.getMessage()); //TypeError: Shape.getMessage is not a function

Shape.getMessage() throws an error - TypeError: Shape.getMessage is not a function

This is because getMessage is not a static function so it cannot be accessed by class name Shape.

We can fix this issue by making the below modification:

  getMessage() {
    return `Shape is of ${Shape.color} color` ;
  }
Inheritance for static fields and methods

Static fields and methods are inherited from the parent class.

  class Rectangle extends Shape {

  }
  console.log(Rectangle.color); // blue
  console.log(Rectangle.getColor()); // blue
  console.log('color' in Rectangle); // true
  console.log('getColor' in Rectangle); // true
  console.log('getMessage' in Rectangle); // false

Static private fields and methods

ES2022 adds the ability to make fields and methods private.

Like private instance fields and methods, static private fields and methods are also defined with a hash (#) prefix.

class Shape {
  static #color = 'blue';

  static #getColor() {
    return this.#color;
  }

  getMessage() {
    return `Shape is of ${Shape.#getColor()} color` ;
  }
}
//console.log(Shape.#color); // Throws error
//console.log(Shape.#getColor()); // Throws error
const shapeInstance = new Shape();
shapeInstance.getMessage(); // Shape is of blue color
//console.log(shapeInstance); //Shape {}
Inheritance for private static fields and methods

Private static fields have a restriction. Only the class which defines the private static field can access the field. This can lead to unexpected behavior when using this.

  class Shape {
    static #color = 'blue';
    static #getColor() {
      return this.#color;
    }
    static getMessage() {
      return `Shape is of ${this.#color} color` ;
    }
    getMessageNonStatic() {
      return `Shape is of ${this.#getColor()} color` ;
    }
  }
  class Rectangle extends Shape {}

  console.log(Rectangle.getMessage()); //
  const rectangle = new Rectangle();
  //Rectangle.getMessage(); // Uncaught TypeError: Cannot read private member #color from an object whose class did not declare it
  console.log(rectangle.getMessageNonStatic()); // TypeError: Cannot read private member #getColor from an object whose class did not declare it

In the above example, this refers to the Rectangle class. It does not have access to the private field #color. When we try to call Rectangle.getMessage(), it cannot read #color and throws TypeError.

We can fix the issue by modifying code as below -

  class Shape {
    static #color = 'blue';
    static #getColor() {
      return this.#color;
    }
    static getMessage() {
      return `Shape is of ${Shape.#color} color` ;
    }
    getMessageNonStatic() {
      return `Shape is of ${Shape.#getColor()} color` ;
    }
  }
   class Rectangle extends Shape {
  }
  console.log(Rectangle.getMessage()); //Shape is of blue color
  const rectangle = new Rectangle();
  console.log(rectangle.getMessageNonStatic()); //Shape is of blue color

Static field proposal is stable and with a variaty of implementations shipped.

Image Credit

Check out our previous blogs on private fields and, public, private instance fields to know more about class fields(public and private) which would be included in ECMAScript 2022.