Dapeng Li

Hungry, foolish and passionate – yet another software developer.

Just Enough JavaScript – Prototype and Inheritance

one comment

This is the third and last post on Just Enough JavaScript, we’ll cover Prototye and Inheritance in JavaScript.

Prototype

Prototype: an early sample or model built to test a concept or process or to act as a thing to be replicated or learned from. (From Wikipedia)

Classical vs. Prototypal

In a classical programming language, you specify the structure and behavior of a type of objects in the definition of a class, by instantiating new objects of that class (usually with the new keyword to invoke the constructor defined in the class), you get objects sharing the same behavior but with inner states of their own. You can also specify a class to inherit from another class, constructing a class hierarchy, child class will has access to state and behavior of parent class. When a property is accessed from an object, it will be first looked for in the class of that object; if it’s not there, it will be looked for in the parent class of that object – the property will be looked up in the class hierarchy.

JavaScript takes a different approach supporting the code reuse and inheritance parts, it’s prototypal. Although JavaScript has the new keyword and the concept of constructor, there is no class in the language. In JavaScript, you create a new object, specify its state and behavior (properties); then use that object as a prototype, you create new objects just like that one. When a property is accessed from an object, it will be first looked for in the object itself; if it’s not there, it will be looked for in the prototype of that object – the property will be looked up in the prototype chain. Without an abstraction of class, the prototypal approach is actually simpler.

What’s the difference between classical and prototypal?

In my opinion, the goals of both approaches are the same – to achieve code reuse and a mechanism for expressing inheritance hierarchy. When programming in an object-oriented programming language (classical and prototypal are just different paradigms implementing object-orientation), you rarely have an object of its own kind, instead you create objects of the same kind (with different inner states, but similar behaviors); from generalization to specialization, you need an inheritance hierarchy.

The prototypal way

Here’s one flow to create a new object based on a prototype in JavaScript.

  1. Create an object p, which will be used as prototype
  2. Create a function to create new objects, which will set the prototype of newly created object to p
  3. Use the function in #2 to create new objects

#1 is easy to achieve, we can create new object with object literals, but what about #2 and #3?

Fortunately JavaScript has language features for #2 and #3, the bad news is those features are a little bit confusing.

Remember constructor (discussed in last post on function)? It’s used for creating new objects, that’s for #3. It turns out constructor also has a property called prototype, when you set an object (in our case, p) to the prototype property of a constructor, new objects created with that constructor will have their prototype set to be the object p.

// ex_3.1
// prototypal_way_new_object

var p, Ctor, obj1, obj2;

p = {  // Our prototype object
	name: "p"
};

Ctor = function () {  // Constructor
	this.greeting = function () {
		return "Hello " + this.name;
	};
};

Ctor.prototype = p;  // Set prototype property of Ctor to be p

obj1 = new Ctor();
obj1.name = "obj1";
console.log(obj1.greeting());  // Hello obj1

obj2 = new Ctor();
console.log(obj2.greeting());  // Hello p

If you update the prototype property of constructor (not replacing it), all objects created with that constructor will take effect immediately. Prototype chain is “live”, as in the following example.

// ex_3.2
// prototypal_way_change_existing_object_augment_prototype

var p, Ctor, obj, obj2;

p = {  // Our prototype object
	name: "p"
};

Ctor = function () {  // Constructor
	this.greeting = function () {
		return "Hello " + this.name;
	};
};

Ctor.prototype = p;  // Set prototype property of Ctor to be p

obj = new Ctor();
console.log(obj.greeting());  // Hello p

Ctor.prototype.age = 10;  // Update (augment) the prototype object

Ctor.prototype.howOld = function () {
	return this.age + " years old";
};

p.goodbye = function () {  // Or update prototype object directly
	return "Bye " + this.name;
};

console.log(obj.greeting());  // Hello p
console.log(obj.howOld());  // 10 years old
console.log(obj.goodbye());  // Bye p

obj2 = new Ctor();
console.log(obj2.greeting());  // Hello p
console.log(obj2.howOld());  // 10 years old
console.log(obj2.goodbye());  // Bye p

However, if you replace the prototype property of constructor, objects created after that will use the new prototype, existing objects will not. Consider this example:

// ex_3.3
// prototypal_way_change_existing_object_replace_prototype

var p, Ctor, p2, obj, obj2;

p = {  // Our prototype object
	name: "p"
};

Ctor = function () {  // Constructor
	this.greeting = function () {
		return "Hello " + this.name;
	};
};

p2 = {
	age: 10,
	howOld: function () {
		return this.age + " years old";
	}
};

Ctor.prototype = p;  // Set prototype property of Ctor to be p

obj = new Ctor();
console.log(obj.greeting());  // Hello p

Ctor.prototype = p2;  // Change the value of prototype property of Ctor to be p2

obj2 = new Ctor();
console.log(obj2.howOld());  // 10 years old

console.log(obj.howOld());  // Uncaught TypeError: Object #<Object> has no method 'howOld'

Here’s the confusing part: the constructor (it’s a function object) has its own prototype. The prototype of constructor has nothing to do with the prototype property of the constructor, the former is part of the prototype chain of the constructor object, the latter points to an object used as prototype for new objects created with the constructor.

Please take a moment to read this post on Understanding JavaScript Prototypes from Angus Croll, it sheds more light on this concept, and the remaining of this post will depend some contents not covered here (such as the [[prototype]] property), but they’re well explained in Angus’ post.

Here’s a diagram showing the relationship of constructor and object, simplified from our ex 3.1.

Prototype

One pseudoclassical way

Here’s a quote from Douglas Crockford 1:

JavaScript itself is not confident in its prototypal nature, so it offers an object-making syntax that is reminiscent of the classical languages. Few classical programmers found prototypal inheritance to be acceptable, and classically inspired syntax obscures the language’s true prototypal nature. It is the worst of both worlds.

Maybe because many developers are more familiar with the classical syntax, there are a lot of ways to emulate class in JavaScript.

Here’s the pseudoclassical code generated by CoffeeScript (line breaks added/removed for readability by me), the CoffeeScript code is class B extends A.

The goal of the code in the example below is to create a class B, which inherits from class A. Objects of class B will have access to properties of both B and A.

The implementation in the code generated by CoffeeScript is to update the prototype chain of constructor B: set the prototype of the object referenced by prototype property of constructor B to be the same as the object referenced by prototype property of constructor A.

The __extends() function below does that job, it accepts two constructor functions as arguments.

// ex_3.4
// pseudoclassical_CoffeeScript_class_B_extends_class_A

var B,
	__hasProp = {}.hasOwnProperty,
	__extends = function(child, parent) {
		for (var key in parent) {
			if (__hasProp.call(parent, key))
				child[key] = parent[key];
		}

		function ctor() { this.constructor = child; }

		ctor.prototype = parent.prototype;

		child.prototype = new ctor();

		child.__super__ = parent.prototype;

		return child;
	};

B = (function(_super) {
	__extends(B, _super);

	function B() {
		return B.__super__.constructor.apply(this, arguments);
	}

	return B;
})(A);

Here is the diagram showing the relationships between objects in ex 3.4, before the effect of __extends() function.

Inheritance before

Here is the diagram showing the relationships between objects in ex 3.4, after the effect of __extends() function.

Inheritance after

There’re many more patterns for inheritance, but I’m fine to stop here.

Final Thoughts

This post concludes our short journey with JavaScript.

Someone said JavaScript is perhaps the most widely used and most reviled programming language on the modern Web.

It’s interesting to learn about how Brendan Eich created the language with a tight deadline and other constraints. 2

I also realized that there are more factors than technology have impact on the evolving of a programming language such as JavaScript. (You can Google for “ECMAScript 4 Oslo”)

The good news is we’ll see more features be added to JavaScript, and developers will have better options to build a more beautiful web.


1. More details from page 29 in the book JavaScript: The Good Parts by Douglas Crockford, Amazon Link

2. More details from page 133 in the book Coders at Work: Reflections on the Craft of Programming by Peter Seibel, Amazon Link

Written by Dapeng

May 17th, 2012 at 8:12 pm

Posted in Programming

Tagged with

  • Sagar Suryawanshi

    Hey Nice article. I have one question as my code not working.. I have used prototype method for this…

    h4 {font-family: sans-serif;}

    h3 {font-family: sans-serif;}

    p {font-family: sans-serif;}

    a {font-family: sans-serif; color:#d15423; text-decoration:none;}

    HTML5 Canvas Example – Smooth Dragging with Object Oriented Programming

    /// CustomerBooking class

    function CustomerBooking(bookingId, customerName, film, showDate)

    {

    }

    CustomerBooking.prototype.getCustomerName = function()

    {

    return this.customerName;

    }

    CustomerBooking.prototype.getShowDate = function()

    {

    return this.showDate;

    }

    CustomerBooking.prototype.getFilm = function()

    {

    return this.film;

    }

    CustomerBooking.prototype.getBookingId = function()

    {

    return this.bookingId;

    }

    // cinema class

    function cinema()

    {

    this.bookings = new Array();

    }

    cinema.prototype.addBooking = function(bookingId, customerName, film, showDate)

    {

    this.bookings[showDate] = new CustomerBooking(bookingId,

    customerName, film, showDate);

    }

    cinema.prototype.getBookingsTable = function()

    {

    var booking;

    var bookingsTableHTML = “”;

    for (booking in this.bookings)

    {

    bookingsTableHTML += “”;

    bookingsTableHTML += this.bookings[booking].getBookingId();

    bookingsTableHTML += “”;

    bookingsTableHTML += “”;

    bookingsTableHTML += this.bookings[booking].getCustomerName();

    bookingsTableHTML += “”;

    bookingsTableHTML += “”;

    bookingsTableHTML += this.bookings[booking].getFilm();

    bookingsTableHTML += “”;

    bookingsTableHTML += “”;

    bookingsTableHTML += this.bookings[booking].getShowDate();

    bookingsTableHTML += “”;

    bookingsTableHTML += “”;

    }

    bookingsTableHTML += “”;

    return bookingsTableHTML;

    }

    var londonOdeon = new cinema();

    londonOdeon.addBooking(sag, sam, sap, san);

    var sag = document.getElementById(“one”).getAttribute(“value”);

    var sam = document.getElementById(“two”).innertext;

    var sap = document.getElementById(“three”).value;

    var san = document.getElementById(“four”).value;

    function outputt() {

    document.write(londonOdeon.getBookingsTable());

    }

    clcik

    Thanks, Please revert me back.

    sagar