JavaScript Objects and Private Data

Charles Ging
The Startup
Published in
6 min readJul 5, 2019
Photo by Waldemar Brandt on Unsplash

In this brief article, I will attempt to illustrate differences in JavaScript’s treatment of private data using 2 of its object creation patterns. In doing so, I hope to expose a few tradeoffs between the 2 patterns when encapsulated private data is desired.

First, we will briefly examine the 2 object creation patterns in question before introducing how each can address private variables. For an excellent, more detailed examination of these patterns check out Fundamental Object Design Patterns in JavaScript.

Pseudo Classical Pattern

Using the pseudo-classical pattern of object creation, we (1) instantiate objects with unique property values using a constructor function, and (2) define shared behavior on the constructor’s prototype object. It is critical to understand that each object created from a constructor has a __proto__ property which references the prototype object of the constructor function. This allows the delegation of behaviors across a hierarchy of chained prototype objects.

When we invoke describe on our Car object mySedan, our object checks to see if it was defined with a method named “describe” (it wasn’t). Our object then checks its __proto__ object where the method is found, and executed.

Objects Linking to Other Objects (OOLO)

Using this pattern, we fully embrace JavaScript’s prototypal inheritance and distance ourselves further from class-based models of object creation. Using OOLO, we define an object that will be used as a “blueprint” prototype for other objects. Using Object.create(), we create a new object whose __proto__ object references the object passed to create.

In order to provide our created object with its own unique property values, we define an init function which is invoked separately from the instantiation of the object. This adds an additional step to the creation of unique objects compared to the pseudo-classical pattern’s usage of constructor functions with the new operator.

Below, we see a few objects created from the Car “mold” in ex2, the properties of these objects, and whether the instances can be determined to have been created from Car:

Introducing private data to these patterns:

Using our Car example, let’s add a private variable speed whose value can only be updated using the method accelerate.

  • The Pseudo-classical pattern:

My first inclination was to declare a local variable speed whose value be tracked by way of a closure, and our accelerate method:

This accomplished our goal of creating a private variable speed. The expense of this solution, however, is that all objects created from Car hold copies of our accelerate, and viewSpeed methods. This is not exactly a DRY solution and could. With the addition of more private variables, additional functions will be required which, in this example, would be owned by every instance of the Car object. Perhaps we can write these methods on the constructor’s prototype to avoid duplication of code:

Here, we discover that our local variable speed is out of scope within the prototype methods. This issue can be remedied by adding setter and getter methods to the constructor, which reintroduces methods into the constructor.

Adding a setter and getter for speed to the constructor adds unnecessary code rendering our accelerate method written on the prototype redundant since the value of speed can be reassigned directly using the setSpeed method:

In the last 2 examples, we moved our accelerate to the constructor’s prototype, but then had to reintroduce code (the setters and getters) in the constructor to access our speed variable. This does not seem to be the DRY solution we’re looking for.

In another article on JS object creation patterns, John Dugan explores the “Durable Constructor Pattern” and how it may provide an object with private data accessible via methods on the object. This pattern is similar to an object factory in that the constructor is invoked without the new operator, and returns a new object. This pattern makes use of a closure between the methods defined on the new object, and the private variable. One upside to this approach is its legibility — the private variable, and the methods with access to it are nicely bundled. The downside is that since we do not invoke the constructor with the new operator, there is no relationship between the object returned and the constructor function, ie, the prototype object of Car is unavailable to Car objects:

Takeaways from the pseudo-classical pattern:

It seems that using the pseudo classical pattern of object creation often requires that our objects all hold copies of functions in order to interact with private variables. Put another way, the methods that have access to an object’s private data need to exist in the same function scope. As a result, we wind up with duplicate code in our object’s instances. If our application only requires a few instances of the Car object, this duplication is not very costly, but in an application with millions of Car objects, a DRYer solution would be better. Compared with object creation patterns that leverage prototypal inheritance to define shared behaviors in a single location, it is redundant for every instance of an object type to carry a copy of a shared behavior.

  • The OLOO (Objects Linked to Other Objects) Pattern:

Using this pattern, we use Object.create(obj) to create an object using obj as its prototype.

So, we know that our Car prototype must return an object to be used as our instances’ prototype. All shared behavior can be written on the Car prototype, and unique object property values can be initialized using an init method.

Here, we have written our accelerate method on the Car (prototype) object. We somehow need to be able to create a private scope that tracks the speed of our car instances…

~ Enter the IIFE ~

Immediately Invoked Function Expressions are functions like any other function, except that they are invoked immediately after definition. An IFFE is a function expression appended with () to immediately invoke it.

IFFEs are often used to create a private scope since, as functions, they create a new scope. If we think back to the requirements of our OOLOCar object creation, we stated that it needs to return an object to be used as our instances’ prototype. If we create an IFFE that returns our prototype object, we can declare other “private” variables scoped within the IFFE, but outside of the returned prototype object. Using closure, our prototype’s methods retain access to the variables in scope at the time of function declaration. Presumably, this means we could create a local variable speed accessible via closure by our prototype’s methods:

Unfortunately, as the above example illustrates, our 2 car objects retain access to the same speed variable. As such, each object does not track a unique speed value since in this example, speed operates as something of a class variable which can be updated by any instance of the “class”.

One solution to this dilemma is to leverage this “class variable” to hold the private data for all instances of Car. In this example, each instance of Car will need a unique id property used to lookup its associated private data. Therefore, we will use a privateData lookup object that will hold the speed property (among other private properties) of a given instance:

Takeaways from this implementation of the OOLO Pattern:

To me, the biggest benefit of this solution over our pseudo-classical pattern solution is that we have a working Car prototype which eliminates the need for every instance to hold a copy of shared Car behaviors (like the accelerate function). The drawback here is that the private data for every instance of Car is centralized within the privateData object. Additionally, we’ve added more “setup” code during object instantiation: we need to create and assign an id for every car, and map each instance to an object within privateData.

I hope this basic examination private data in 2 different object creation patterns has helped you develop a basic understanding of these patterns as well as an appreciation for JavaScript’s complexity in regards to private data!

Please comment with any feedback about my examples, or examples of your own!

--

--