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 (ES13) 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 when one object in the set referenced by a given WeakMap instance goes out of scope, it can be garbage collected whether the instance is referencing other live objects or not.

This is not the case with a Map object. If a Map object references a set of objects, they must all go out of scope before any of them can be garbage collected. In other words, the “dead” 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 documentation for private class features.