Private Members in Javascript Classes

01/12/2019 — 2 Min Read — In Javascript

Unfortunately there is no native private properties in Javascript.

So sad

But there is a few way to mimic that behavior.

1. Scoped variables

Don't attach the new properties to the object, but keep them inside a class constructor.

class SomeClass {
constructor(prop) {
var _prop = prop;
this.getProp = function(prop) {
_prop = prop;
};
this.setProp = function() {
return _prop;
};
}
}
const obj = new SomeClass('stuff');
console.log(obj._prop); // undefined
console.log(obj.getProp); // 'stuff'

I know, I know. Defining functions within a constructor looks ugly. If you want to make prop full private you must delete getProp & setProp functions and define all other functions that use prop in the constructor meaning you're recreating them with every instance. This is a performance and memory penalty, but in some use cases the penalty is acceptable.

2. WeakMap

A WeakMap can be used to avoid the previous approach's performance and memory penalty. WeakMaps associate data with class instances in such a way that it can only be accessed using that WeakMap. So, 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 your instances can share a single WeakMap, so you don't need to recreate methods just to make them access their own WeakMaps.

let SomeClass = (function() {
let privateProps = new WeakMap();
class SomeClass {
constructor(name) {
this.name = name; // this is public
privateProps.set(this, { age: 20 }); // this is private
}
greet() {
// Here we can access both name and age
console.log(`name: ${this.name}, age: ${privateProps.get(this).age}`);
}
}
return SomeClass;
})();
let joe = new SomeClass('Joe');
joe.greet(); // name: Joe, age: 20
// here we can access joe's name but not age

The privacy of this approach could theoretically be breached by tampering with the global WeakMap object. That said, all JavaScript can be broken by mangled globals. Our code is already built on the assumption that this isn't happening. (This method could also be done with Map, but WeakMap is better because Map will create memory leaks unless you're very careful, and for this purpose the two aren't otherwise different.)