Private class fields are a Stage 3 TC39 proposal. Even though they're still experimental, you can use private class fields in Node.js 12 without flags or transpilers. In this article, I'll explain the basics of private class fields and how they interact with existing paradigms, like Object.keys() and assert.deepStrictEqual().

Your First Private Class Field

ES6 classes allow you to declare fields on the class. Fields are own properties on instances of the class, so hasOwnProperty() returns true for class fields.

class MyClass {
  myField = 42;
}

const x = new MyClass();
x.myField; // 42
x.hasOwnProperty('myField'); // true

x.myField = 43;
x.myField; // 43
x.hasOwnProperty('myField'); // true

As specified as of 2019, private fields are fields whose name starts with #. For example, the below class has a private field #b.

class Foo {
  #b = 15;

  get() {
    return this.#b;
  }

  increment() {
    ++this.#b;
  }
}

Private fields are not visible outside of the class. Outside the class, obj['#b'] is undefined and the #b field is not an own property.

const obj = new Foo();

obj['#b']; // undefined
obj.hasOwnProperty('#b'); // false

JavaScript will throw an error if you attempt to access a private field outside of a class using .:

// SyntaxError: Undefined private field #b: must be declared in an enclosing class
obj.#b;

Implications For Node.js

Private class fields are invisible outside the class, and there's no back door to access them. So functions like Object.keys() and Object.getOwnPropertyDescriptors() don't see private class fields.

const obj = new Foo();

Object.keys(obj); // []
Object.getOwnPropertyDescriptors(obj); // {}

Private fields are useful for encapsulating internal state. Functions like assert.deepStrictEqual() don't have access to private class fields, so you can now compare two objects while ignoring internal state.

const obj1 = new Foo();
const obj2 = new Foo();

assert.deepStrictEqual(obj1, obj2);

obj1.increment();
obj1.get() === obj2.get(); // false

// Succeeds even though `#b` has a different value
assert.deepStrictEqual(obj1, obj2);

Moving On

Private class fields are still a Stage 3 proposal, which means the spec may change and/or the spec might never be formally adopted. Don't build your entire production application on private class fields. But, now that they're included in Node.js 12 without any flags, try them out and see what you think!

Found a typo or error? Open up a pull request! This post is available as markdown on Github
comments powered by Disqus