The JavaScript Pseudoclassical Object Creation Pattern: A Step-by-Step Walkthrough

As I was progressing through the Launch School curriculum on Object Oriented JavaScript, things were going pretty smoothly. Until, that is, I encountered the Pseudoclassical Object Creation Pattern.

If you’re familiar with JavaScript, you’re likely aware that there are a number of different patterns that can be used to create an object: Factory Function, Object Linking to Other Objects (OLOO), and Pseudoclassical. Each method varies in complexity. Today I’m going to focus on the method I had the most trouble wrapping my head around: the Pseudoclassical pattern.

The biggest hurdle to comprehending this pattern well is that there are just so many moving pieces to keep track of at once. Add to this any preconceptions you may have regarding object instantiation coming from a class based programming language, and a couple of poorly named object properties, and it all becomes too much to hold in your head at once.

In order to fully grasp how it all worked, I had to first break the process of creating an object in the Pseudoclassical pattern out into its constitute components. Then, I watched how each of these components evolved throughout execution. Only after documenting the entire process was I finally able to reason what was actually going on and why.

This article assumes you already have some knowledge of object creation in JavaScript. Perhaps you already know of the pseudoclassical pattern but are struggling, like I did, to understand it.

Here is a list of those components and a quick definition of each:

1. the ‘constructor function’

A constructor function is just a regular function. The only reason it’s called something different is because it’s intended to be used only for constructing new objects. Convention dictates that you capitalize constructor functions. This doesn’t have any meaningful effect on the program, it’s purely to indicate the intent of the function to other programmers.

// This is a function
function dog() {
  this.speak = ‘bark’;
}
// And this is a constructor function
function Dog() {
  this.speak = ‘bark’;
}

2. the function ‘.prototype’ property

In JavaScript, functions are objects. When you define a function, it’s automatically given a property called .prototype. This property points to another object that is used only when the function is invoked as a constructor function with the new command. Think of this object as the ‘DNA’ that will get passed along to new objects created by this constructor function.

That .prototype object contains a property of its own called .constructor and it references the function itself.

Dog =
  .prototype = {
    .constructor = Dog
  }

3. the object [[prototype]] property

Every object in JavaScript has, by default, a property called [[prototype]]. You can’t access it directly, but you can access it indirectly, in most browsers today, by using the getter/setter property .__proto__. The [[prototype]] property references an object that contains its DNA. That same DNA I just mentioned above.

puppy =
  .[[prototype]] = {
    .constructor = Dog
  }
I’ve purposely left out some properties from the definitions above for the sake of making it easier to focus on the components most relevant to creating an object with this pattern.

With the preamble out of the way, let’s now go step-by-step through the process of creating a new object using the Pseudoclassical Object Creation Pattern.

1. Declare a ‘constructor function’ called Dog:

function Dog() {
  this.speak = ‘bark’;
}

This is pretty straight forward. We’re simply initializing a function called Dog. It’s just a regular function, but because we plan to use it to create Dog objects, we call it a constructor function.

I’ve included the property speak so that we can track exactly where properties and methods end up as they are added to the object (or made available to the object). This was a point of confusion for me.

2. Once declared, the Dog constructor function has these properties:

Dog =                      // These are the same Dog functions
  .prototype = {
    .constructor = Dog     // These are the same Dog functions
  }

Here we are looking at the properties of the function Dog. Remember, functions are objects too so they can have properties of their own.

Upon being defined, the Dog method was automatically assigned the .prototype property and the object it points to.

3. Create a new object and assign it to a variable called ‘puppy’:

var puppy = new Dog();

4. When the ‘new’ command was invoked, the following sequence of events occurred:

this = {};

First, a brand new, empty object is created and assigned to this.

this.[[prototype]] = Dog.prototype;

Second, the new object’s [[prototype]] property is assigned a reference to the Dog function’s .prototype object. This will ensure that the new object now has a link back to the object from which is was created. In other words, this is the point where the new object gets a link to its parent’s DNA.

this.speak = ‘bark’;

Third, the Dog.prototype.constructor property is referenced (remember, that simply points to the Dog method) and invoked, at which point the method is executed with our new object (referenced by this) as the context. As we go through the method’s code, the property speak is added to the new object and assigned a reference to the String ‘bark’.

return this;

Finally, the new object is returned.

5. Once created, the new Dog object, ‘puppy’, has these properties:

puppy =
  .[[prototype]] = {
    .constructor = Dog
  }
  .speak

It’s worth noting that the puppy object’s [[prototype]] property is referencing the exact same object (highlighted in bold) that Dog.prototype references. Thus, we can now confirm that the the puppy object has a direct link to the DNA of its parent, the Dog function.

Also notice that the speak property was added to the puppy object itself.

6. Add a new property to a single dog object:

puppy.run = ‘running’;

Now, let’s say we wanted to add a new property or method to the puppy object. Simply add the property as you would to any other object.

7. Once added, the ‘puppy’ object now has these properties:

puppy =
  .[[prototype]] = {
    .constructor = Dog
  }
  .speak
  .run

Notice that, once again, the property was added directly to the puppy object. We could call either of the puppy object’s methods as expected:

puppy.speak;   // returns 'bark'
puppy.run;     // returns 'running'

8. Add a new property that ALL Dog objects can access:

Dog.prototype.jump = ‘jumping’;

We add the jump property to the Dog function’s .prototype object, not the Dog function object itself. If we added the property to the Dog function object, it would only be available to the Dog function object. We want to add the property to the Dog function’s DNA so that all Dog objects can access it, and that DNA is stored in the Dog function’s .prototype object.

9. Once added, the constructor function, Dog, now has these properties:

Dog =
  .prototype = {
    .constructor = Dog
    .jump = ‘jumping’      // Property was added here
  }
  // Not here!

Notice that this time the new property was added to the Dog function’s .prototype object, not to the puppy object itself and not to the Dog function object either.

10. Now, the ‘puppy’ object has these properties:

puppy =
  .[[prototype]] = {
    .constructor = Dog
    .jump = ‘jumping’
  }
  .speak
  .run

We can now see that the puppy object has access to speak and run as they are properties of its own.

The puppy object also has access to the jump property via its [[prototype]] object. This is because the puppy object’s [[prototype]] property is pointing to the exact same object as the Dog function’s .prototype property (both highlighted in bold, above and below). Any property we add to the Dog.prototype object will be available to every other object that has access to that same Dog.prototype object. This includes all objects created with the Dog constructor function of course.

Dog =
  .prototype = {
    .constructor = Dog
    .jump = ‘jumping’
  }

11. If we were to now create a new Dog object, ‘puppy2’, it would have these properties:

puppy2 =
  .[[prototype]] = {
    .constructor = Dog
    .jump = ‘jumping’
  }
  .speak

The puppy2 object has access to the jump property via its link to the Dog.prototype property. The puppy2 object also has access to the speak property that was added directly to the new puppy2 object when that object was constructed as described in step 4.


Other Considerations

You might be wondering, as I did, why don’t we just declare every method or property we want Dog objects to have right within in the Dog function itself (like we did with the speak property)? Why bother messing around with the Dog.prototype object when every new Dog object is going to get all the properties outlined in the Dog function anyway.

There are at least two reasons:

  1. Adding the same behavior to every new Dog object is not very efficient. It means that each of those objects takes up slightly more memory because they’re all hauling around their own copies of the same behavior. By putting all of the common behavior into a single object, then sharing that object with any other object that needs access to that behavior, you’re maximizing the use of your available resources.
  2. The code will be easier to maintain when you’re consistent in terms of where an Object’s behavior is grouped. Having some of it in the Dog constructor method and some of it in the Dog.prototype object would not be ideal.

Advice on Next Steps

Getting comfortable with the Pseudoclassical Object Creation pattern will likely take some time. Hopefully this step-by-step walkthrough on the subject will get you a bit closer to mastery of this often bewildering pattern.

The way I’ve described things here is just one perspective out of many others available to you on the internet. What I would recommend, and what helped me as well, was reading through a few different articles on the subject in order to attack the issue from multiple fronts.

Here are a couple of resources you might consider (as written or recommended by my colleagues and instructors at Launch School):

If you have any questions (or corrections), please leave them in the comments below and I’ll be happy to help where I can.

Good luck and happy coding!