MENU
Private Members
A private member is a property or method which can only be accessed by methods within the class. As of July 2017, there is no native support for private properties and methods. Symbols can be used to provide 'an extra level of privacy', by preventing the keys/properties of an object from being exposed through some popular methods such as for...in, Object.keys() and JSON.stringify().
let Person = (function () {
let age = Symbol(); // declared in another module perhaps?
class Person {
constructor(n, a) {
this.name = n;
this[age] = a;
}
introduce() {
console.log(`My name is ${this.name}. I am ${this[age] - 10}.`);
}
}
return Person;
})();
var j = new Person('Jane', 45);
j.introduce();
console.log(JSON.stringify(j));
console.log(Object.keys(j));
console.log(j[Object.getOwnPropertySymbols(j)[0]]);Although given an object per se, such properties can still be exposed through Proxy (8.21), Object.getOwnPropertySymbols(), etc., there is no natural means to access them through a few direct methods, which may be sufficient sometimes from an OOP perspective.A more effective alternative is to use a scoped WeakMap (8.15), which is not enumerable at all.
let Person = (function () {
let privateProps = new WeakMap();
class Person {
constructor(name) {
this.name = name; // this is public
privateProps.set(this, {age: 45}); // this is private
}
greet() { // Here we can access both name and age
console.log(`I am ${this.name}.
I am ${privateProps.get(this).age-10}.`);
}
}
return Person;
})();
let j = new Person('Jane');
j.greet();
console.log(j.privateProps);Since ECMAScript 2022, private fields using the # syntax are natively supported, making the Symbol- and WeakMap-based workarounds above largely unnecessary. A name prefixed with # (eg. #v2) declares a private field that can only be read or written from inside the body of the class where it is declared — not even subclasses can access it directly.
class C1 {
v1 = 1; // public, accessible everywhere
#v2; // private, accessible only within this class
constructor(value) {
this.#v2 = value;
}
}
class C2 extends C1 {
constructor() {
super();
// this.v3 = this.#v2; // error
}
}
const o = new C2;
console.log(o.v1);
// console.log(o.#v2);Besides private instance fields, ECMAScript 2022 also allows private methods and private accessors (getters/setters), all declared with the # prefix and all only callable from within the class body.
class Counter {
#count = 0; // private field
#step() { return 1; } // private method
get #doubled() { return this.#count * 2; } // private getter
increment() {
this.#count += this.#step();
return this;
}
get value() { return this.#doubled; } // public method exposing private state
}
var c = new Counter();
c.increment().increment();
console.log(c.value);
// c.#step(); // SyntaxError outside the class bodyTo test whether an arbitrary object has a given private field, without risking a TypeError, ECMAScript 2022 also added the 'ergonomic brand check': #field in obj.
class Point {
#x;
constructor(x) { this.#x = x; }
static isPoint(obj) { return #x in obj; } // brand check, works even if obj is null or unrelated
}
console.log(Point.isPoint(new Point(1)));
console.log(Point.isPoint({x: 1}));
console.log(Point.isPoint(null));