Classical OOP in JavaScript

03 December 2010

There are two ways to look at the Object-oriented programming when it comes to JavaScript. The first one is to think in terms of classes and instances (the classical OOP), and the second one - only in terms of objects (the prototypal OOP). It is hard for Java, C# and C++ developers not to think about classes, since the classical OOP, as we all know it from these languages, relies entirely on defining the instance structure through classes and interfaces. Thus, I will first make some notes on it on this article.

Classical OOP

In classical OOP, we define a class via the keyword function. The same keyword is used to define public and private, static and instance methods. It sounds complicated, and it really is. But the reason for this is that the classical OOP in JavaScript is only a patch.

The following is an example of a fully exposed class:

var Book = function(isbn, title, author) { 
	this.isbn = isbn; 
	this.title = title || 'No title specified'; 
	this.author = author || 'No author specified';
}
// public instance method
Book.prototype.display = function() { ... };
// public static method
Book.convertToTitleCase = function(string) { ... }; 

Instances are created via the new keyword, as you may guess. The anonymous function is the class constructor.

All instances of the Book class provides three public properties (isbn, title and author) and a public method (display). The prototype property is used to attach a method to all instances of a given class (the prototype property is still to be discussed in details).

There is nothing interesting about the fully exposed classes, so we are getting straight to the private members.

var Book = function(isbn, title, author) { 
	// private attributes
	var isbn, title, author; 
	// private method
	function checkIsbn(isbn) { ... }
	// public privileged method
	this.setTitle = function(newTitle) { 
		title = newTitle || 'No title specified'; 
	}; 
};
// public non-privileged method
Book.prototype.display = function() { ... }; 

This time, isbn, title and author are private attributes. Book is in fact a closure for the public setTitle method. Thus, the setTitle method has access to the local variables of its closure. Using closures, we can imitate private attributes and methods.

Two types of public methods can be declared in JavaScript, in contrast with Java. This is not some native feature of the language, it's just that attaching a method to the this object inside the class (the closure) makes the method (called privileged method) see the private members, and attaching a method to the prototype property of the class is done outside the class (the closure) and that's why the method (called non-privileged method) does not see the private members. There are no non-privileged methods in Java.

Now, how do we declare private static members? The most famous example about a static property is a counter that counts all instances.

var Book = (function() {
		// private static attribute
		var instances = 0;
		// class constructor
		return function() { 
			instances++;
		}
})();

var book1 = new Book(); // Book.instances == 1
var book2 = new Book(); // Book.instances == 2

Note the () at the end of the function declaration. This is a self-executing function. The question is what happens to the Book variable. Our self-executing function returns another function. It is assigned to the Book. The idea is that you cannot execute the outer function any more (it does not have a name). So the local variable instances is initialized only once (this is exactly the behavior of the static members within a class). But its value can be changed by calling the inner function. Remember, the inner function has access to the local variables of its closure. You call it by instantiating the Book class.

So, speaking in terms of the classical OOP, Book is a class, instances is a private static property, and we have a constructor inside the Book class.

Static methods are declared in the same way:

var Book = (function() {
	// private static method
	function checkISBN(isbn) { ... }
	// class constructor
	return function(isbn) { 
		checkISBN(isbn);
		...
	}
})();
// public static method
Book.convertToTitleCase = function(string) { ... }

At this moment you should have noticed that we used different structures to declare static members and instance members - self-executing function and a "simple" closure. So how do we combine instance and static members? It's simple, we use the structure for the static members (a self-executing function) and declare the instance members inside the returned inner function (the constructor):

var Book = (function() {
	// private static method
	function checkISBN(isbn) { ... }
	// class constructor + instance members
	return function(isbn) { 
		// constructor code
		checkISBN(isbn);
		// private method
		var isbnToCountry = function() { ... }
		// public privileged method
		this.getCountry = function() { ... }
	}
})();

In the end, what is an Object-oriented paradigm without inheritance?

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

We need an Author class that extends the Person. We do it in that way:

function Author(name, books) {
	Person.call(this, name); 
	this.books = books; 
}
Author.prototype = new Person(); 
Author.prototype.constructor = Author; 

Several things happen here. First, we don't have a native method to declare that a class extends another one. We need to rely on the prototype chaining. When we call a member on an object, the interpreter (the browser) first checks whether the method is available in this particular object. If not found, the special prototype property is used. If the value of the prototype property is object, the member is searched in this object. And the process continues to the top (while the prototype of some object in the chain is null).

When an object is created via the new keyword, a secret link is created between the new object and its class (the class is in fact a function, and all functions in JavaScript are objects). This explains why public methods are added to the instances of a particular class by adding them to the prototype property of the class. Now, get back to the beginning of the Classical OOP section to once again see the examples (and this time think only in terms of objects to understand the prototype chaining and why public instance methods are defined by attaching them to the prototype property of the "class").

Let's get back to out Author. We want all members of Person to be available to instances of Author. So we link the prototype property of Author to a new Person object. This looks strange to a classical programmer, but remember, we have to attach an actual object, and new Person is exactly that.

For every given function f, f.prototype.constructor points by default to the f itself. When the new operator is used, the interpreter looks for the value of the f.prototype.constructor. We have just changed it by changing the entire prototype property, so we need to correct the behavior of the new operator.

Finally, we need to call the super class constructor. This is also complicated. The magic function call is used. It changes the context of the Person function to the context inside the Author function. I.e. the this keyword within Person points to the same object as the this keyword within the Author.

Because of all these efforts, the string 'Dustin Diaz' is returned by the following code:

(new Author('Dustin Diaz', ['Pro JavaScript Design Patterns'])).getName();

Nice. Now let's encapsulate all the inheritance logic inside a global function extend:

function extend(subClass, superClass) {
	subClass.prototype = new superClass();
	subClass.prototype.constructor = subClass;
	subClass.superClass = superClass;
}

This works nice, except that may not be so efficient as this one:

function extend(subClass, superClass) {
	var temp = function() {};
	temp.prototype = superClass.prototype;
	subClass.prototype = new temp();
	subClass.prototype.constructor = subClass;
	subClass.superClass = superClass;
}

What we do here is to avoid instantiating the super class and in the same time preserve the prototype chain. As a result we can write the following code:

function Author(name, books) {
	Author.superClass.call(this, name); 
	this.books = books; 
}
extend(Author, Person);
 

I personally prefer to write Author.extends(Person) instead of extend(Author, Person). It even sounds better when you pronounce it (try to pronounce both loud). And it easy to be implemented. Just add a method extends on every function:

Function.prototype.extends = function(superClass) {
	var temp = function() {};
	temp.prototype = superClass.prototype;
	this.prototype = new temp();
	this.prototype.constructor = this;
	this.superClass = superClass;
}

By this time you should know how to declare and instantiate classes, how to define private and public, static and instance members, and how to extend one class from another. The true power of Object-oriented programming is seen when you apply the concepts to some design pattern. Singleton is used more in JavaScript in comparison with any other language. So here is a quick example:

var multipleEdit = function() {
	var CHECKBOX_CONTAINER_ID = 'params';
	var checkboxes;
	function initCheckboxes() { ... };
	function attachClickEvents() { ... };
	...
	return {
		install: function() { ... };
		selectRecord: function() { ... };
	}
}

This is a reusable module for checkbox handling. There is some complex logic here, but the bottom line is that we want to provide only two public methods: install (to initialized the script) and selectRecord (in case someone wants to trigger the event registration from within the html). Hence, we declare these methods inside a return statement. Again, it may looks like a magic for the most programmers who come from Java or C++. In order to understand the structure, just think in terms of JavaScript. multipleEdit is just a name of an anonymous function. All variables and functions declared inside this function are not accessible outside. The function returns another variable to the outside environment: an object with two properties: install of type function, and selectRecord of type function. The tricky part is that this object is created dynamically, using the {} notation. As a result, we can write the following code to initialize the script: multipleEdit.install().

Prototypal OOP

In the prototypal OOP, you directly create an object instead of defining its structure through a class. Then, you reuse it to create new objects and rely on the power of the prototype chaining. An article on prototype OOP in JavaScript is coming soon.

Resources

Most of the examples in this article are from the book Pro JavaScript Design Patterns by Dustin Diaz and Ross Harmes. I really liked the first part of the book, explaining both ways to think of OOP in JavaScript.