Private fields, methods and, accessors in JavaScript


Private methods proposal, as a part of the class fields proposal has been promoted to Stage 4 in the TC39 process and will be officially included in ECMAScript 2022!

In this blog, we will cover private class features which will be a part of ECMAScript 2022.

Before

By default, all properties in ES6 classes are public and can be examined or modified outside the class. In the example below, we find that there is nothing in the class to prevent the property from being changed without calling the setter.

class TimeTracker {
  name = 'Alice';
  project = 'blog';
  hours = 0;

  set addHours(hour) {
    this.hours += hour;
  }

  get timeSheet() {
    return `${this.name} works ${this.hours || 'nothing'} hours on ${this.project}`;
  }
}

let person = new TimeTracker();
person.addHours = 2; // standard setter
person.hours = 4;    // bypass the hours setter altogether
person.timeSheet;     // Alice works 4 hours on blog.

After

Starting from ECMAScript 2022, private class fields are defined using a hash # prefix, From the above example, we can modify it to include a private class field to prevent the property from being changed outside of the class method.

class TimeTracker {
  name = 'Alice';
  project = 'blog';
  #hours = 0; //private class field

  // private method (can only be called within the class)
  set addHours(hour) {
    this.#hours += hour;
  }

  get timeSheet() {
    return `${this.name} works ${this.#hours || 'nothing'} hours on ${this.project}`;
  }
}

let person = new TimeTracker();
person.addHours = 4; // standard setter
person.timeSheet // Alice works 4 hours on blog

When we try to modify the private class field outside of the setter method, we get the error -

person.hours = 4 // Error Private field '#hours' must be declared in an enclosing class

We can also make methods or getter/setters private, by giving them names starting with #.

class TimeTracker {
  name = 'Alice';
  project = 'blog';
  #hours = 0; //private class field

  // private method (can only be called within the class)
  set #addHours(hour) {
    this.#hours += hour;
  }

  get #timeSheet() {
    return `${this.name} works ${this.#hours || 'nothing'} hours on ${this.project}`;
  }

  constructor(hours) {
    this.#addHours = hours;
    console.log(this.#timeSheet);
  }
}

let person = new TimeTracker(4); //Alice works 4 hours on blog

Private class features are under development or implemented in below -

  1. Babel by Tim McClure
    • Private methods added in v7.2.0
    • Private accessors added in V7.3.0
  2. In progress in TypeScript by Bloomberg
  3. Shipping in V8

Notes:

  • Private fields and methods must be declared up-front in the field declaration.
  • Private fields cannot be created ad-hoc through the assignment.

Check out more details in the private methods proposal.