How to create private members in a JavaScript Class

linda H
linda H
May 25 · 3 min read

First off, all the way to ES6, there was no official support for private members in JavaScript class.

There are, however, a couple of ways to act like we have one.

With Closure

JavaScript’s closure comes naturally a “scope” where your variable lives. If we define a class member in a right place, we can have private-like member.

There are three ways to do this.

1. Constructors

function ConstructorCar(initValue) {
this.publicMember = initValue;
let _privateMember = initValue; //private member
this.getPrivateMember = function() { return _privateMember; }
this.setPrivateMember = function(v) { _privateMember = v; }
}
ConstructorCar.prototype.drive = function() {
//this._privateMember undefined
console.log(“Drive! “+this._privateMember);
}
const car2 = new ConstructorCar(100);
console.log(“getPrivateMember: “+car2.getPrivateMember());
car2.drive();

2. Classes

class ClassCar {
constructor(mileage) {
this.mileage = mileage; //this is public
var privateMember = mileage;//this is private
this.getPrivateMember = function() { return this.privateMember; }
this.setPrivateMember = function(v) { this.privateMember = v; }
}
drive() {
this.mileage ++;
console.log(‘Drive! ‘+this.mileage);
console.log(‘privateMember? ‘+this.privateMember); //undefined
}
}
const car1 = new ClassCar(100);
console.log(“privateMember:”+car1.privateMember);//undefined
car1.getPrivateMember();
car1.drive();

3. Factories

//Factory
let FactoryCarProto = (function() {
var mileage = 0;
var _privateMember = 0;
const FactoryCarProto = {
mileage,
setMileage(m) {
this.mileage = m;
},
drive() {
this.mileage ++;
console.log(‘Drive !’);
},
setPrivateMember(v) {
_privateMember = v;
},
getPrivateMember() {
return _privateMember;
}
};
return FactoryCarProto;
})();
function factoryCar() {
return Object.create(FactoryCarProto);
}
const car3 = factoryCar();
car3.drive();
car3._privateMember; //undefined
car3.setPrivateMember(100);
car3.getPrivateMember();

The Closure approach has a downside where, for methods to have access to private data they must be created inside the constructor, this means we are recreating them with every instance. There will be memory and performance impact.


With WeakMap

This approach is build on top of Closure approach.

Here we use the scoped variables method to create a private WeakMap, then use that WeakMap to retrieve private data associated with this.

This is faster than the scoped variables method because all the instances can share a single WeakMap, so we don’t need to recreate methods every time we create a instance.

let Person = (function () {
let privateProps = new WeakMap();
class Person {
constructor(name) {
this.name = name; // this is public
privateProps.set(this, {age: 20+name}); // this is private
}
greet() {
console.log("Hello: "+privateProps.get(this).age);
}
getAge() {
return privateProps.get(this).age;
}
setAge(age) {
privateProps.set(this, {age: age+name});
}
}
return Person;
})();
let joe = new Person(‘Joe’);
console.log(“joe.age? “+joe.age); //undefined;
joe.greet();

If you think WeekMap way is ugly, our next approach is even worse...


With Scoped Symbols

We can use Symbol as property name like below.


let ClassCar = (function() {
let privateMemberKey = Symbol(‘privateMember’);
class ClassCar {
constructor(mileage) {
this.mileage = mileage; //this is public
this[privateMemberKey] = mileage;//this is private
}
drive() {
this.mileage ++;
console.log(‘Drive! ‘+this.mileage);
console.log(‘privateMember? ‘+this[privateMemberKey]);
}
}
return ClassCar;
})();
const car1 = new ClassCar(100);
car1.drive();

However, there is a way to bypass it:

//this will give out private member keysObject.getOwnPropertySymbols(car1)

So it’s not really 100% private.

Getting tired of all of these workarounds?

Good news!

ESnext is here and JS finally has support for private members!

class MyClass {
a = 1; // .a is public
#b = 2; // .#b is private
static #c = 3; // .#c is private and static
static d = 4;
x = 100;
incB() {
this.#b++;
}
getB() {
return this.#b;
}
getC() {
//this.#c ++; //this will error out
return this.#c;
}
/*#incX() { //this won't work
this.x++;
}*/
}
MyClass.d; //4
var myc = new MyClass();
myc.getC(); // 3

However …

ESnext still doesn’t have support for private function — the #incX won’t work.

And always keep in mind, not all browsers support the new standard (yes, IE, I’m talking about you).

JavaScript in Plain English

Learn the web's most important programming language.

linda H

Written by

linda H

Tech Lead @ Ubiquity6, Unity Technologies, Salesforce … I build things, font-end, back-end, they run on PC, on your phone, Raspberry Pi, …

JavaScript in Plain English

Learn the web's most important programming language.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade