Defining Instance and Static Fields in JavaScript

Modern JavaScript introduces class field declaration syntax, allowing developers to define properties directly within the class body without requiring explicit assignment inside the constructor. This syntax standardizes how both instance members (belonging to each object instance) and static members (belonging to the class constructor itself) are created. By supporting both public and private visibility, class fields provide a cleaner, more declarative way to structure state in object-oriented JavaScript.

Instance Fields

Instance fields represent properties that are created and maintained separately on each instantiated object.

Public Instance Fields

Public instance fields are declared directly in the class body with an optional initializer:

class User {
  role = 'guest'; // Public instance field
  
  constructor(name) {
    this.name = name;
  }
}

const user1 = new User('Alice');
console.log(user1.role); // "guest"

When an instance is created, public instance fields are initialized automatically. In a derived class (using extends), these fields are set immediately after super() returns.

Private Instance Fields

Private instance fields are declared using a hash # prefix. These fields cannot be accessed, modified, or detected from outside the class lexical scope:

class BankAccount {
  #balance = 0; // Private instance field

  deposit(amount) {
    this.#balance += amount;
  }

  getBalance() {
    return this.#balance;
  }
}

const account = new BankAccount();
account.deposit(100);
console.log(account.getBalance()); // 100
// console.log(account.#balance); // SyntaxError: Private field '#balance' must be declared in an enclosing class

Static Fields

Static fields belong to the class function itself rather than to instances of the class. They are shared across the application and are commonly used for caching, fixed configuration, or factory helpers.

Public Static Fields

Public static fields are declared by prefixing the field name with the static keyword:

class Configuration {
  static defaultTimeout = 5000; // Public static field
}

console.log(Configuration.defaultTimeout); // 5000

const config = new Configuration();
console.log(config.defaultTimeout); // undefined

Public static fields are attached directly to the constructor function at the time the class definition is evaluated.

Private Static Fields

Private static fields combine the static keyword with the # prefix. They are accessible only by methods defined within the class:

class Counter {
  static #count = 0; // Private static field

  constructor() {
    Counter.#count++;
  }

  static getCount() {
    return Counter.#count;
  }
}

new Counter();
new Counter();
console.log(Counter.getCount()); // 2
// console.log(Counter.#count); // SyntaxError

Key Differences and Initialization Order

  1. Evaluation Timing: Static fields are initialized once, when the class definition is executed by the JavaScript engine. Instance fields are evaluated and assigned every time a new instance is created via the new keyword.
  2. Access Scope: Instance fields are accessed via instance references (this.field or instance.field), whereas static fields are accessed via the class constructor (Class.field) or within the class definition.
  3. Encapsulation: Using the # identifier enforces hard privacy at the language level for both instance and static members, preventing direct external access through reflection or property inspection.