Thursday, March 12, 2015

JavaScript: Object-oriented (OO) programming

JavaScript has a decidedly functional programming leaning. But that does not mean that Objected-oriented paradigm cannot be used effectively in JavaScript applications.

Object orientation is naturally suited for applications such as games and simulations. In order to effectively utilize JavaScript in OO context, it is useful to understand how JavaScript handles data and functions.

JavaScript data types

JavaScript data types include: string, number, boolean, and object

var aString = "abc";
var aNumber = 123;
var aBool = true;
var anObject = { val1: "abc", val2 : true };
  • An object in JavaScript contains name-value pairs.
  • Objects can be nested inside other objects
var anObjectInside = { someString : "abc", someBool : true, 
   someObject : { x: 1} };

We can create an array of any data type. In addition, an array can contain items of different data types. Internally, arrays are simply JavaScript objects whose members can be accessed using a numeric index.

var anArray = [aString, aBool, anObject];

Functions as first class citizens

In JavaScript, a function can be assigned to a variable. The function can be invoked through the variable.

var aFunc = function() { console.log("hello") ;};
aFunc();  // invoke the function

First-pass: Building an object in JavaScript

Using the above information, we can build an object (not class) in JavaScript.

Let's say we want to build a circle object that knows its radius and can calculate its area and circumference.

// Javascript object that contains name-value pairs
// "radius" contains a numeric value of 4
var circle = { radius : 4 };

// "area" and "circumference" point to functions
// a function inside an object can reference other
// name-value pairs in the same object using "this"
// keyword (this.radius, for example)
circle.area = function() {
    return 22/7 * this.radius * this.radius ; };
circle.circumference = function () {
    return 2 * 22/7 * this.radius; }

// print out the result
console.log("area = " + circle.area());
console.log("circumference = " + circle.circumference());

// change radius and print out results
circle.radius = 6;
console.log("area = " + circle.area());
console.log("circumference = " + circle.circumference());

In the above code, we have hand-built a custom instance of an object. But what we really need is a way to construct multiple instances of an object from a class definition.

Second-pass: Defining a class and creating class instances

What if we wanted to create and retain many circle objects simultaneously? Or in other words, how do we automatically create area and circumference methods for all future instances of  a circle class. JavaScript provides some syntactic sugar to accomplish this via prototype.

In JavaScript, we also employ closures to mimic OO classes. Simply stated, in  a function closure, nested functions have access to vars and parameters of parent function even after parent function is executed/returned. In the following example, radius is variable defined for the parent function Circle that is accessible to child functions area and circumference.

// JavaScript Constructor function is used to create a 
// boilerplate (class) for an object type
// Constructor initializes properties of an object
//   when the Constructor is called preceded by "new"
var Circle = function (radius){
this.radius = radius;  // create and assign data member
};
// Through "prototype", functions (methods in OO terms) 
// can be added to every new instance of the object
Circle.prototype.area = 
  function() { return 22/7 * this.radius * this.radius; };
Circle.prototype.circumference = 
  function() { return 2 * 22/7 * this.radius; };

// Create and use multiple objects of Circle class type
var circle1 = new Circle(4);
var circle2 = new Circle(6);

// object properties can be accessed using the dot notation
console.log("circle 1 radius = " + circle1.radius);
console.log("circle 1 area = " + circle1.area());
console.log("circle 1 circumference = " + circle1.circumference());

// object properties can also be accessed using the indexer ["radius"]
console.log("circle 2 radius = " + circle2["radius"]);
console.log("circle 2 area = " + circle2.area());
console.log("circle 2 circumference = " + circle2.circumference());
A class defined using the constructor and prototype is still a plain old JavaScript object. Consider the following code:
circle1.anewproperty = 404;
console.log(circle1.anewproperty);
This code just defined a new property (a name-value pair) on a Circle object even though we never created such a property in the Constructor function.

valueOf member

Special member of object. Returns the numeric value of the object.

var obj = {
  valueOf: function () {return 1; }
}

var total = 4 + obj;
console.log(total);

Output:
5