Object-oriented JavaScript

This article assumes that you already have a good idea of what object-oriented programming is and experience in JavaScript programming.

Before writing the 103683rd blog article/web page about object-oriented JavaScript (more than 1,640,000 on Google at this date), let’s list couple of good resources about this topic which influenced this article :

What is an object ?

How do we want to use objects ?

From the object-oriented programming perspective, an object has a state (described by several attributes) and a behavior (described by several methods/functions). From an external point of view, an object can be manipulated through the visible parts of the object.

When defining an object, one natural question is : what should be visible from the outside ? The answer to this question depends one the way you want outsiders to use your object, what service(s) your object is supposed to provide.

Usually, for maintenability purposes, objects should only provide public (available from outside the object) methods and no public attributes. Private (usable only inside the object) attributes are used to describe the object state and private methods are used for modularity purposes. Forbidding public attributes can be justified rigourously. This is not the point of this article.

Several objects “following the same pattern” are usually defined through a class. An object defined through a class is said to be an instance of the class. In most object-oriented languages, instanciations are done through the new operator :

myObj = new className();

In this case, a special public method called constructor is usually called to initialize the object state.

Let’s sum this up. About objects, I expect to be able to :

  • Create objects (private attributes, public/private methods)
  • A class mecanism with constructors

These are my current high-level expectations of object-oriented programming. Of course, object-oriented programming is richer than what I have described and I am forgetting inheritance on purpose for the moment. I will discuss this in another article.

ECMAScript potential

Here is the ECMAScript 5 definition of an object :
“An ECMAScript object is a collection of properties each with zero or more attributes that determine how each property can be used (…). Properties are containers that hold other objects, primitive values, or functions. (…) A function that is associated with an object via a property is a method.”
For the purpose of this article, the ECMAScript “properties” are what I earlier called attribute and ECMAScript “attributes” are something I will describe in another article.

Filling the gap

Let’s see several ways of defining objects in JavaScript and judge how they can fit our aformentioned high-level expectations of object-oriented programming.

“JSON” way

One way to define object is to define it in an inlined way. Example :

var o = {prop1 : 25,
         prop2 : "blabla",
         method1 : function(){alert(this.prop1 + this.prop2.toString());}
         };

However, with this approach, everything is public. There is no way to define private properties describing the state of this object and shared by the public methods.

Dynamic construction

JavaScript allows to dynamically add attributes and methods to the objects.

var o = {}; // Creates an empty object
o.prop1 = 25;
o.prop2 = "blabla";
o.method1 = function(){alert(this.prop2 + this.prop1.toString());};

Same conclusion, everything is public. It is worse than the previous solution in the sense that each attribute/method requires the execution of one instruction while with the previous solution, the object is created once for all and there is only one assignment.

Some privacy, please !

As seen in the previous article, creating an anonymous function called once allows privacy. As a consequence, we have a pattern to create objects with private attributes:

var o = (function(){
    var privateAttribute; /* Defines a private attribute that can be used in public/private functions thanks to JavaScript closures */

    function privateMethod(){
    };

    return {
        publicMethod1 : function(){
                                   },

        publicMethod2 : function(){
                                   }
    };
})();

Private attributes only (unless you intentionally declare a public one in the returned object), public and private methods. The requirements for the object are fulfilled. By the way, this is an implementation of the singleton design pattern.

JavaScript style classes

Despite the fact that the class keyword is a reserved one in ECMAScript, there is currently no direct way to create classes as it is possible in C++ or Java. However, ECMAScript permits the use of “constructors”. Those are functions defined like any other function. The difference resides in the usage :

function f(){
}

f(); /* calls the f function */


function myClass(){
/* Let's manipulate the "this" object. Add attributes/methods to it */
    this.method = function(){};
}

var o = new myClass(); /* Using the new operator changes the semantic of myClass to be a constructor returning the "this" object */
o.method();

As a consequence, a pattern to create objects from “classes” emerges :

function myClass(a){
    var privateAttribute = a;

    function privateMethod(){
        alert(privateAttribute);
    };

    this.publicMethod = function(){privateMethod();};
})();

var o1 = new myClass("azerty");
var o2 = new myClass("qwerty");

o1.publicMethod(); // alerts "azerty"
o2.publicMethod(); // alerts "qwerty"
/* o1.privateMethod is undefined as well as o1.attribute */

Public/private methods, private attributes, everything is here, but…

The constructor ambiguity

In Java or C++, the constructor is a function of the class called at instanciation time. As a function, local variables (such as a loop counter) can be declared in it and are well distinguished from private attributes. Without extra care, this is not the case for JavaScript. This is due to the confusion between JavaScript “class” definition which is done through a so-called “constructor” and the notion of constructor in the Java or C++ sense.

function myClass(s, n){
    var myString;

    this.getMyString = function(){return myString};

    /* let's construct the string composed of n repetitions of the s string */

    var i; // loop counter, of course
    myString = "";

    for(i = 0 ; i < n ; i++){
        myString += s;
    }
}

In this example, we can see that myString initialisation is done inside of the JavaScript constructor, but not inside an actual function with local variable definition. As a result, i is defined in the same way that myString while their semantics are different. The former was here temporarly for initialization and doesn’t reflect the object intern state while the latter does.

Anonymous constructor

Here is a solution to separate local variables of a constructor function and private attributes.

function myClass(s, n){
    var myString;

    this.getMyString = function(){return myString};

    /* let's construct the string composed of n repetitions of the s string */
    (function(){
        var i; /* loop counter.
                 Available only within the scope of the anonymous function */
        myString = "";
        for(i = 0 ; i < n ; i++){
            myString += s;
        }

        alert(this.getMyString());
    }).call(this);
}

The constructor in the Java/C++ sense is the anonymous function. One little issue is that without any addition, inside the anonymous function, the this keyword doesn’t refer to the object we are creating. As a consequence, public methods are not available. The .call(this) calls the anonymous function and imposes that the this keyword inside of the function refers to the object in parameter. In this case, this which is the object we are creating.

We have objects with private attributes, public and private methods. We have “classes” and constructors. That’s it for the moment.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s