I don’t know about you, but when I started to learn JavaScript, there were a few things that really confused the hell out of me. And the worst part is that the internet really didn’t help with this issue of mine. My confusion was formed mostly around Objects. There are a lot of different ways to create objects and because the community has different opinions on how it is best done, things can get pretty puzzling. You will see a lot of influential developers like Kyle Simpson or Eric Elliott that will advocate different styles of coding, like never use the class keyword or don’t create objects using the new operator because it’s a wrong way to code and so on. I will not argue with their decisions but one thing is for sure, their statements can get pretty baffling.

I think it’s only fair to look on how JavaScript deals with objects so we can form our own idea on the whole problem. Let’s review the different ways of creating objects in JavaScript to underline the whole idea.

The 2 main players in our game

  • functions
  • objects

Why are functions so important? Because they create objects. A function that creates objects is called a constructor function. If you stick the new operator in front of any function, that function will become a constructor function and it will spit out a new object. Don’t believe me? Try it.

function randomFunction() {}

const obj = new randomFunction();  

It doesn’t make much sense to use it like this but it still works.

Creating objects using constructor functions

JavaScript doesn’t have real classes, that’s a clear fact. For the sake of argument, let’s call a class the blueprint of the future created instance. This means that when an instance is created, the blueprint can’t be modified. Also the instance looses any connection to that blueprint. In JavaScript however, we have only objects, constructor functions and linkage. Ever wondered what that magical thing called prototype is? Drums rolling… the prototype is the linkage I am talking about. In simple terms, the prototype is the linkage between two objects.

const obj = new Object();  
// same thing with
const literal = {};  

You would want to say that we just instantiated the obj object using the Object class. That is not true. We created an object and we linked it’s prototype to Object‘s prototype. Object is not a class, but a constructor function.

So what happens when we call the new keyword ? It’s no magic:

  1. A new obejct is created
  2. The construction function is called with the context bounded to the newly created object
  3. The prototype of the newly created object will be linked to the prototype of the constructor function
function Cat(name) {  
   this.name = name;
}

Cat.prototype.getName = function() {  
   return this.name;
}

const jimmyTheCat = new Cat('Jimmy');  

Every newly created object has a special attribute which shows this prototype linkage. And that special attribute is __proto__. So in our case, the following expression will return a true value:

console.log(jimmyTheCat.__proto__ === Cat.prototype);  

This is how prototypes works. That’s it.

And with ES2015 new class keyword, things got a bit simpler. We can rewrite our code like so:

class Cat {  
   constructor(name) {
      this.name = name;
   }

   getName() {
      return this.name;
   }
}

const jimmyTheCat = new Cat('Jimmy');  

So what do we have in common between these two ES5 and ES6 examples? Everything! The same steps I described earlier apply exactly the same. The class syntax is just sugar coding.

console.log(jimmyTheCat.__proto__ === Cat.prototype);  

But what if you want to create objects without a constructor function?

I’m glad you asked. You can do this using the Object.create ( ) method. Let’s say we have a simple EventEmitter object and we want to create a more complex PubSub object (if you are familiar with the Publish/Subscribe pattern). The base functionality is on the Event Emitter so why not start from there. This is how our first object would look like:

Cool. Let’s use this object to create a new object:

const PubSub = Object.create(EventEmitter);  

Now we can customize our new object:

PubSub.publish = (channel, cb) => {  
   this.on(channel, cb); 
}
PubSub.subscribe = (channel, payload) => {  
   // Maybe add some cooler features like patterns
   this.trigger(channel, payload);
}

So what did we just do? How will the PubSub object look like:

At this point we created a new object starting from a given object which will act as our new prototype. Confusing? Let me make it easier. Here is how the Object.create function looks like:

Object.create = (o) => {  
   const F = function () {};
   F.prototype = o;
   return new F();
};

Black magic right? This function does exactly what we want. Create a new object, without a construction function, but still be linked to a prototype. Well as you can see, behind the scenes, there is still a constructor function being called, however, the end result is what we would expect.

But what if I want to combine two objects without any linkage?

I’m glad you asked that too.

If you used Angular, lodash, underscore or other frameworks/libraries, then you surely used the extend (dest, src) function. This function simply copies every enumerable keys from one object to another. It merges two objects if you will. But since ES2015 we now have this function native in JavaScript: Object.assign ( ).

This method is great if you want to modify values on an object without modifying the original object:

const user = { firstName: 'Jon', lastName: 'Doe' , debt: 100 };  
const eraseDebt = (user) => Object.assign({}, user, { debt: 0 });  
const noDebtUser = eraseDebt(user);

console.log(noDebtUser.debt === user.debt); //false  

For more complex examples you can visit the official docs.

To summarize

JavaScript is very permissive in terms of how you write your code. You can use a more classic OOP style of coding or you can go full functional programming. This is the beauty of it. One might argue that one approach is better than the other, but I myself can’t say which one is better. I think the most important part is to be consistent with your code and understand how things actually work.

Andrei Cacio