Thursday, March 5, 2015

Class style programming in JavaScript: Part 1

JavaScript does not have language support for classes in the same way that Java does[1]
But, JavaScript is a versatile language. It can be tailored to mimic different programming paradigms. The Java-style class-based programming pattern is quite popular. So much so, that there are dedicated modules in frameworks such as Dojo declare[2], Prototype's Class.create and the list goes on. This pattern is also commonly referred to as Object Oriented programming, which I think is a misnomer because it really is more about classes than objects. 
In JavaScript, functions play the role of..well.. plain old functions and also to create class-like entities.For this post, we are interested in the latter. When a function is invoked with the "new" keyword, it returns an object i.e. it acts as a constructor.
Lets look at an example:
1
2
3
4
5
6
7
8
9
var Car = function(numGears){
 this.numGears = numGears;
 this.drive = function(){
   console.log("driving");
 }
};

var myCar = new Car(4);
myCar.drive() //driving
Whoa.. the above type of Car cannot be stopped, because there is no method/mechanism in the Car function to do that.
Lets add a stop() method to that
1
2
3
myCar.stop = function(){
 console.log("stop");
}
Do you see a problem with this... yes, only myCar can be stopped now, not the rest of the cars coming out of the factory. Lets see if this assumption is true.
1
2
var yourCar = new Car(5);
yourCar.stop();// TypeError: Object [object Object] has no method 'stop'
So, we need this method on all cars that come out of the factory. If the stop() method were defined as part of the Car function definition in the first code snippet, then all new cars would "inherit" this, but how do we install a "stop" method now. The answer lies in the prototype property available on functions:
console.log(Car.prototype);// Object {}
The prototype property is available on all functions, but make sense only for functions that are used as constructors. The reason will be obvious when you understand their role.
The prototype property on functions point to an object which serves as the the blueprint of the objects created using that function. The newly created objects have a secret link to this blueprint.
But why is it empty in the example .. shouldnt we be seeing a drive() method on it ?
No, because we did not leverage this  prototype property to add the method to this blueprint. Look back at the first code snippet, the drive() method is defined like so:
this.drive  = function(){..
This results in every car instance getting its own copy of the drive() method. (Obviously this is not very efficient. We will correct this later.)
To understand how the prototype property works, lets add stopping functionality to the Car.
1
2
3
4
Car.prototype.stop = function(){
  console.log("stop");
}
yourCar.stop(); // stop
Note that unlike the drive() method, we added the stop() method to the prototype property. This has the effect that all instances get this method including the ones that were created before this method was added
So, that worked.  
Lets paint a picture to remember this better


If we were to redo the Car function from scratch, this is how it would look:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
var Car = function(numGears){
 // per-instance properties
 this.numGears = numGears;
};

// shared code on prototype
Car.prototype.drive= function(){
  console.log("driving");
}
Car.prototype.stop = function(){
  console.log("stop");
}

var myCar = new Car(3)
myCar.drive(); // driving
myCar.stop(); // stop
This is quite strange-looking, very unlike a Java class.  But it is the way you are expected to do class-based programming in this language.
In another post, I will explore how code is shared by inheritance using the class style paradigm

[1] In the next version of the language i.e. in ES2015 / ES6, you can create classes using the class keyword. Here is a link where you you learn more about it:
Classes in JavaScript - My understanding is that it is mostly syntactic sugar that transforms into the above code.
[2] I originally intended to write about the Dojo declare feature, hence the url of this page name reads as "dojo-declare". The declare module in Dojo is used for creating Java-style classes. The widget framework  - Dijit uses this heavily.

No comments:

Post a Comment