Source: A List Apart Blog

A List Apart Blog Prototypal Object-Oriented Programming using JavaScript

Douglas Crockford accurately described JavaScript as the world's most misunderstood language. A lot of programmers tend to think of it as not a "proper" language because it lacks the common object-oriented programming concepts. I myself developed the same opinion after my first JavaScript project ended up a hodgepodge, as I couldn't find a way to organize code into classes. But as we will see, JavaScript comes packed with a rich system of object-oriented programming that many programmers don't know about.Back in the time of the First Browser War, executives at Netscape hired a smart guy called Brendan Eich to put together a language that would run in the browser. Unlike class-based languages like C++ and Java, this language, which was at some point called LiveScript, was designed to implement a prototype-based inheritance model. Prototypal OOP, which is conceptually different from the class-based systems, had been invented just a few years before to solve some problems that class-based OOP presented and it fit very well with LiveScript's dynamic nature.Unfortunately, this new language had to "look like Java" for marketing reasons. Java was the cool new thing in the tech world and Netscape's executives wanted to market their shiny new language as "Java's little brother." This seems to be why its name was changed to JavaScript. The prototype-based OOP system, however, didn't look anything like Java's classes. To make this prototype-based system look like a class-based system, JavaScript's designers came up with the keyword new and a novel way to use constructor functions. The existence of this pattern and the ability to write "pseudo class-based" code has led to a lot of confusion among developers.Understanding the rationale behind prototype-based programming was my "aha" moment with JavaScript and resolved most of the gripes I had with the language. I hope learning about prototype-based OOP brings you the same peace of mind it brought me. And I hope that exploring a technique that has not been fully explored excites you as much as it excites me.Prototype-based OOPConceptually, in class-based OOP, we first create a class to serve as a "blueprint" for objects, and then create objects based on this blueprint. To build more specific types of objects, we create "child" classes; i.e., we make some changes to the blueprint and use the resulting new blueprint to construct the more specific objects.For a real-world analogy, if you were to build a chair, you would first create a blueprint on paper and then manufacture chairs based on this blueprint. The blueprint here is the class, and chairs are the objects. If you wanted to build a rocking chair, you would take the blueprint, make some modifications, and manufacture rocking chairs using the new blueprint.Now take this example into the world of prototypes: you don't create blueprints or classes here, you just create the object. You take some wood and hack together a chair. This chair, an actual object, can function fully as a chair and also serve as a prototype for future chairs. In the world of prototypes, you build a chair and simply create "clones" of it. If you want to build a rocking chair, all you have to do is pick a chair you've manufactured earlier, attach two rockers to it, and voilà! You have a rocking chair. You didn't really need a blueprint for that. Now you can just use this rocking chair for yourself, or perhaps use it as a prototype to create more rocking chairs.JavaScript and prototype-based OOPFollowing is an example that demonstrates this kind of OOP in JavaScript. We start by creating an animal object:var genericAnimal = Object.create(null);Object.create(null) creates a new empty object. (We will discuss Object.create() in further detail later.) Next, we add some properties and functions to our new object:genericAnimal.name = 'Animal'; genericAnimal.gender = 'female'; genericAnimal.description = function() { return 'Gender: ' + this.gender + '; Name: ' + this.name; };genericAnimal is a proper object and can be used like one:console.log(genericAnimal.description()); //Gender: female; Name: AnimalWe can create other, more specific animals by using our sample object as a prototype. Think of this as cloning the object, just like we took a chair and created a clone in the real world.var cat = Object.create(genericAnimal);We just created a cat as a clone of the generic animal. We can add properties and functions to this:cat.purr = function() { return 'Purrrr!'; };We can use our cat as a prototype and create a few more cats:var colonel = Object.create(cat); colonel.name = 'Colonel Meow'; var puff = Object.create(cat); puff.name = 'Puffy';You can also observe that properties/methods from parents were properly carried over:console.log(puff.description()); //Gender: female; Name: PuffyThe new keyword and the constructor functionJavaScript has the concept of a new keyword used in conjunction with constructor functions. This feature was built into JavaScript to make it look familiar to people trained in class-based programming. You may have seen JavaScript OOP code that looks like this:function Person(name) { this.name = name; this.sayName = function() { return "Hi, I'm " + this.name; }; } var adam = new Person('Adam');Implementing inheritance using JavaScript's default method looks more complicated. We define Ninja as a sub-class of Person. Ninjas can have a name as they are a person, and they can also have a primary weapon, such as shuriken.function Ninja(name, weapon) { Person.call(this, name); this.weapon = weapon; } Ninja.prototype = Object.create(Person.prototype); Ninja.prototype.constructor = Ninja;While the constructor pattern might look more attractive to an eye that's familiar with class-based OOP, it is considered problematic by many. What's happening behind the scenes is prototypal OOP, and the constructor function obfuscates the language's natural implementation of OOP. This just looks like an odd way of doing class-based OOP without real classes, and leaves the programmer wondering why they didn't implement proper class-based OOP.Since it's not really a class, it's important to understand what a call to a constructor does. It first creates an empty object, then sets the prototype of this object to the prototype property of the constructor, then calls the constructor function with this pointing to the newly-created object, and finally returns the object. It's an indirect way of doing prototype-based OOP that looks like class-based OOP.The problem with JavaScript's constructor pattern is succinctly summed up by Douglas Crockford:JavaScript's constructor pattern did not appeal to the classical crowd. It also obscured JavaScript's true prototypal nature. As a result, there are very few programmers who know how to use the language effectively.The most effective way to work with OOP in JavaScript is to understand prototypal OOP, whether the constructor pattern is used or not.Understanding delegation and the implementation of prototypesSo far, we've seen how prototypal OOP differs from traditional OOP in that there are no classes-only objects that can inherit from other objects.Every object in JavaScript holds a reference to its parent (prototype) object. When an object is created through Object.create, the passed object-meant to be the prototype for the new object-is set as the new object's prototype. For the purpose of understanding, let's assume that this reference is called __proto__1. Some examples from the previous code can illustrate this point:The line below creates a new empty object with __proto__ as null.var genericAnimal = Object.create(null); The code below then creates a new empty object with __proto__ set to the genericAnimal object, i.e. rodent.__proto__ points to genericAnimal.var rodent = Object.create(genericAnimal); rodent.size = 'S';The following line will create an empty object with __proto__ pointing to rodent.var capybara = Object.create(rodent); //capybara.__proto__ points to rodent //capybara.__proto__.__proto__ points to genericAnimal //capybara.__proto__.__proto__.__proto__ is nullAs we can see, every object holds a reference to its prototype. Looking at Object.create without knowing what exactly it does, it might look like the function actually "clones" from the parent object, and that properties of the parent are copied over to the child, but this is not true. When capybara is created from rodent, capybara is an empty object with only a reference to rodent.But then-if we were to call capybara.size right after creation, we would get S, which was the size we had set in the parent object. What blood-magic is that? capybara doesn't have a size property yet. But still, when we write capybara.size, we somehow manage to see the prototype's size property.The answer is in JavaScript's method of implementing inheritance: delegation. When we call capybara.size, JavaScript first looks for that property in the capybara object. If not found, it looks for the property in capybara.__proto__. If it didn't find it in capybara.__proto__, it would look in capybara.__proto__.__proto__. This is known as the prototype chain.If we called capybara.description(), the JavaScript engine would start searching up the prototype chain for the description function and finally discover it in capybara.__proto__.__proto__ as it was defined in genericAnimal. The function would then be called with this pointing to capybara.Setting a property is a little different. When we set capybara.size = 'XXL', a new property called size is created in the capybara object. Next time we try to access capybara.size, we find it directly in the object, set to 'XXL'.Since the prototype property is a reference, changing the prototype object's properties at runtime will affect all objects using the prototype. For example, if we rewrote the description function or added a new function in genericAnimal after creating rodent and capybara, they would be immediately

Read full article »
Est. Annual Revenue
$5.0-25M
Est. Employees
25-100
CEO Avatar

CEO

Update CEO

CEO Approval Rating

- -/100

Read more