You Can Create Truly Private Properties In JS (without ES6)

Kirill Shestakov
3 min readApr 9, 2017

It’s very common for JavaScript developers to claim that it’s impossible to create truly encapsulated properties and methods on an object and use them on its prototype.

In ES6, there are a few ways of easily achieving private properties without memory leaks. For example, you could use a ES6 Symbol:

Alternatively, you could use a WeakMap:

The problem with the Symbol method is that you can still access those properties using Object.getOwnPropertySymbols. In either case, you would likely have to include bulky polyfills in production code.

Prior to ES6, there was no obvious way to create private properties usable on prototype. The claim is that you either have to abandon the idea or use a memory leaky Map (alternatively, you could use 2 arrays). But what if I told you that there is actually a way to do this that is cross-browser, needs no polyfills or ES6, and doesn’t produce memory leaks?

The idea

I haven’t seen this method used by anyone (EDIT: it was pointed out to me that I wasn’t the first one to come up with this method. Read more here), so I’d like to call it an accessor pattern. The idea is to create a closure, create a key inside the closure and create a storage for private properties that can only be accessed if the correct key is provided. Here’s how you would implement it:

Pretty simple, huh? private function creates the private storage and returns a private access function that will only return the storage if the correct key is provided. Then, in constructor, we assign this private access function to this._ which can be easily used on the prototype, provided that the prototype properties also have access to the key. Basically, there is no way to access the private storage without having the correct key. Hence, if the user tries to call this._ with any argument, maybe with a wrong_key, then the attempt will fail, error will be logged, and all the user will get is undefined.

Advantages of this method:

  • It’s quite simple. All you need is to create a private storage, and you can access all private properties inline within a constructor / prototype.
  • It lets you create truly private properties that can be accessed from the prototype. The user of the class will not be able to access them.

Disadvantage of this method:

  • It slightly pollutes the namespace, because you have to assign the private storage to this._ or alike, but there’s probably no other way to do this.

A problem

A minor problem with this method is that, in case of prototypal inheritance, if both child and parent use the same property name for the private access function (in this example, this._), then the parent’s private properties cannot be accessed within parent’s prototype, because this._will refer to child’s private access function. Here’s what I mean,

When instance.parent_test is called, this._ inside it will refer to the child’s private access function, hence, the key will mismatch and the error will be logged. However, this problem can be quite easily solved.

The final solution

The best solution is to namespace and make sure that parent and child have different property names for their private access functions. Here’s the final solution:

Pretty much the only difference from the previous code snippet is that we replaced this._ for both child and parent classes with this[priv], where priv is namespaced and randomly generated to ensure that private access function is stored under a different property name for child and parent.

Another recommendation I can make is that you should probably secure this[priv] by making it non-configurable, non-enumerable and read-only:

Object.defineProperty(this, priv, {
value: private(key)
})

Instead of just

this[priv] = private(key)

This will make sure that user will not be able to remove or modify this[priv], which is crucial for correct private storage functioning.

Conclusion

Go ahead and use accessor pattern! It allows you to create truly encapsulated properties and use them on a prototype. Let others know about this method so we don’t continue the misconception that privacy is impossible to achieve in JavaScript. Recommending this article will also help 😊

--

--