Code

Obect-Oriented Javascript: Creating and Using Private Data

Private data in objects is a trickier proposition in JavaScript than in most languages. Only the most recent spec (ES12) specifically addresses private data, so the way to hide it has until recently been to put it in a closure.

There are several ways to do this.

Object factories

One way to do this is through an object factory:

In this implementation, fName, lName and honorific are part of the returned object’s closure. As such, they are available to the returned object’s methods, but not exposed as properties of the object.

Constructor function

A constructor function can hold private variables in similar fashion:

Duplication problem with object factories and constructor functions

Both object factories and constructor functions require each object instance to carry its own copy of all the object’s methods. This isn’t very scalable.

The usual way to deal with the duplication problem is to put methods in the prototype. But when there is private data, doing this loses access to the data:

Now that the fullName method is assigned to the prototype, it no longer has access to the Man constructor’s closure. So, it can’t see the private variables.

This is a non-trivial problem in JavaScript. For efficiency reasons, it is preferable to define methods in an object’s prototype, so that every instance shares a single copy. But it is difficult to make private instance data available to the prototype’s methods.

A solution with IIFE and the WeakMap object

Part of a solution is to use an IIFE to wrap private data, a constructor function and prototype methods in a single closure. This is only a partial solution because, while it does manage to expose the private data to the prototype method, it exposes only a single copy of the data to all instances. Therefore, a change by one instance becomes a change to all the others:

A common way to resolve this is to create a single private WeakMap object, and store all the private data in it. A WeakMap object is a set of key/value pairs. While all object instances share the same WeakMap object, the WeakMap object itself is capable of keeping separate objects’ private data separate.

In the WeakMap, each key is an object’s this, and each value is an object containing the object’s private data:

While one may also use the Map object in this way, the WeakMap object is a better solution, because its key/value pairs are able to be garbage collected independently of one another. This means that if a WeakMap object is used to store private data, the lifetime of the data is tied to the lifetime of the object. This is not the case with a Map object, which must go out of scope in entirety before it becomes eligible for garbage collection. This means that if a Map object refers to multiple objects, the objects’ private properties will persist so long as the Map object contains any live references.

The “modern” way: ES12 and the PrivateIdentifier production

With the introduction of the PrivateIdentifier production (as defined by ECMA, a production is an “entity in a context-free grammar”) in ES12, JavaScript has considerably simplified implementation of private properties. Syntactically, the PrivateIdentifier production is implemented as a property preceded by a # character. This syntax is only valid inside a class definition:

Although all popular browsers support this, it is still quite new, and organizations that are conservative about updating their browser versions may not support it.

For more information on browser version compatibility, scroll to the bottom of the doc for private class features.