Ian Cropper

developer

Can we all chill out with prototype?

I've been guilty of it, you've been guilty of it. Every JavaScript developer has been guilty of it. Using prototype everywhere

Look, I get it. We are told to never put functions and variables on the global namespace unless we have a damn good reason for it. Just having a var with no namespace and not attached to a prototype, it can feel dirty, but people please, let's use some discression. 

Have you ever inherited a project and EVERY. SINGLE. FUNCTION. in the JavaScript was attached to an object's prototype? Think about it for minute.....there it is, yes, you remember now. In order to  combat this terrible trend, lets start with some foundation.

What is prototype?

Protoypes are intelligent beings that live within all objects. 

Crap, no those are Midi-chlorians. Prototypes are similar though. Every object in JavaScript contains a prototype. Try it out. Open up the console and paste this in.


var obj = {test: "onetwothree"};
console.log(obj.__proto__);

Not convinced? Then try this.


console.log("".__proto__);

"Wait, even an empty string has a prototype?"

Sure does! If you expand the result, do you see all those properties and functions attached? They're attached to the prototype. Think of them as values attached to an object that every other object inherits from. 

So every object in JavaScript has a prototype. The most notable exceptions are:

  1. Integers
  2. undefined
  3. null

Now before you go thinking "oh, so primitives", no I don't mean that. Try it out. In the console, write:


4.__proto__;
undefined.__proto__;
null.__proto__;

Then write


4.0.__proto__;
true.__proto__;
NaN.__proto__;

I won't go further than that, but it's just interesting to keep in mind. Now where were we?

Oh yeah, So every object in JavaScript has a prototype. So when you create an object and assign something to it's prototype, your'e giving that object something that is accessible only through that object. It's through using prototype that we get the awesome advantages of inheritance in JavaScript. I'll save the inheritance discussion for another time though since this is a post about patterns. The important thing to remember is that once a value is assigned to an object's prototype, it can be accessed within the object using the syntax:

this.someFunctionOrProperty

Here's a simple example:


var util = function(){};
util.prototype.getAllDivs = function() {
	return $('div');
}
utl.prototype.addBorderToAllDivs = function() {
	this.getAllDivs().css("border", "1px solid black");
}
var u = new util();
u.addBorderToAllDivs();

 

The most important part of this post

Look really closely at the code above.

I want you to ask yourself this question: "Does getAllDivs need to be a part of util's prototype?"

WONDERFUL! YOU DID IT! NO! The answer is NO! It doesn't! 

Then why the hell do we write code like this? The answer is, because we love to be able to write the line 

this.getAllDivs()

With that "this" in front of the function call, we know that the function belongs to the object. We like that. It looks good, there's a sense of security there. But this is wrong wrong wrong. I once hacked an online game of Risk because the developer had put everything on the prototype. So when I found the function


.prototype.isAdjacentTo = function(){
  /*[lots of checks and other function calls]*/
} 

I just overrode it to be


.prototype.isAdjacentTo = function(){return true;}

And I was able to attack anyone from anywhere. I tried to convince the other players that I had earned so many points that I had been granted special abilities, but it didn't quite work out. The moderators didn't have any sense of humor.

How can we make that code better?

Let's take advantage of an awesome tool that's super easy to use in JavaScript: a self-executing function that creates a closure.


var util = function(){};
(function(){
    var getAllDivs = function(){
        return $('div');
    }
    util.prototype.addBorderToAllDivs = function() {
        getAllDivs().css("border", "1px solid black");
    }
})();
var u = new util();
u.addBorderToAllDivs();

BAM! exact same functionality, we still have a clean global namespace, and we didn't expose getAllDivs to the rest of the world.

NOTE:

Make sure you think of the "private" variables and functions inside the closure as singletons. The values of these fields will carry over between objects that use them. For example:

<pre class="line-numbers"><code class="language-javascript">
var util = function(){};
(function(){
    var count = 0;
    util.prototype.incrementAndGetCount = function() {
        return ++count;
    }
})();
var u = new util();
u.incrementAndGetCount(); //=> 1
var u2 = new util();
u2.incrementAndGetCount(); //=> 2
</code></pre>

What do I do with this new found knowledge?

You think with it. Rather than just slapping everything on an object's prototype, take a minute and think if it actually needs to be there. 

Remember, prototype is great for inheritance, so if something will never be extending the object, or an object's property will never be used outside of the object itself,  then maybe you don't need that value on the object's prototype.