Object oriented programming in Javascript


this is an adaption of the original article

Object oriented programming in Javascript.

A minimalist approach

Published: 2012-03-01

There are many ways to use object oriented techniques in JavaScript. Still, a lot of people think JavaScript is not an object oriented language because it does not have the concept of a class. In my opinion, the fact that JS does not have a class statement makes JS even more object oriented. While class based languages like Java focus on objects indirectly (you never write objects, you always write classes), JS focuses on objects directly. In JavaScript you deal with real objects instead of classes. Here I present my favourite, minimalist way to work with objects in JavaScript.

Literals

The minimalist approach to write object oriented code in Javascript centers around the concept of literals. Literal objects are created like this:

car = {};
			

Constructors

Because we have no classes in Javascript, the minimalist approach to do OOP in Javascript is to use objects that play the role of classes. We call these objectsbuilders. Builders produce build objects. Builders are nothing more than object factories; objects that produce other objects.

Cat = {
    createNew: function(){
        var cat = { 'name':'Sylvester' };
        return cat;
    }
} 

This object named 'Cat' acts as a class and contains a factory method called 'createNew' that works like a constructor. The builder assembles an object called 'cat' and returns a reference to this newly created instance.

The 'cat' instance returned by 'createNew' looks like:

{ 'name' : 'Sylvester' }

If we ask the cat to print its name; it will print 'Sylvester':

alert( cat.name ); //will print 'Sylvester'

Methods

Let's see how we can add a method to an object in a builder and then invoke that method from outside the builder:

Cat = {
    createNew: function() {
        
        var cat = {}; //create an empty build object
        
        cat.makeSound= function(){ //add logic
            alert('meow');
        }
        
        return cat; //return the fabricate
    }
}
 
var cat = Cat.createNew();
cat.makeSound(); //prints meow

The 'createNew' method is an example of a static method, to mimic static methods simply add functions to the builder object instead of the object that is being built.

Note that we did not use the this-keyword. We don't need it. An instance method may call another instance method by simply referring to the build object:

Cat = {
    createNew: function() {
        var cat = {}; //create an empty object
        
        cat.makeSound = function(){ //add logic
            alert('meow');
        }
        
        cat.makeSoundIndirectly = function() {
        	cat.makeSound(); //use cat instead of this
        }
        
        return cat; //return the fabricate
    }
}
 
var cat = Cat.createNew();
cat.makeSoundIndirectly(); //prints meow

Private properties

Making properties private is easy, because JavaScript has lexical scoping. This means that if you declare a variable or function within a certain lexical scope, only functions in that same block of code will be able to access it. This may sound a little abstract, so here is an example:

Cat = {
    createNew: function() {
        
        var cat = {};
        
        var sound = 'meow'; //sound is local
        
        cat.makeSound = function(){
        
            //can reach sound from here
            alert( sound ); 
        
        }
        
        return cat;
    }
}
 
 
var cat = Cat.createNew();

cat.makeSound();

//but can't reach sound from here
alert(cat.sound); //fail!

The variable 'sound' acts a private property of 'cat'. In contrast to what most people believe about JavaScript it is perfectly possible to create a truly private property like this (actually you can still access it using eval("sound='whaf!'",Cat.createnew); however it's private 'enough' for practical usage, remember that other languages offer reflection techniques to change private properties). The variable 'sound' will not be accessible from outside the 'createNew' function. If you invoke 'makeSound', the property becomes accessible again because 'makeSound' shares the same lexical scope as 'sound'. This way, you mimic the behaviour of a private property.

Inheritance

Instead of creating a brand new 'Cat' object we use the 'Animal' object as our base type, then we add our 'Cat' specific modifications and we are done:

Animal = {
    createNew: function() {
        
        var animal = {};
        
        animal.eat = function(){ alert('eating'); }
        
        return animal;
    }
}
 
Cat = {
    createNew: function() {
        
        var cat = Animal.createNew(); //Inherits
        
        var sound = 'meow';
        
        cat.makeSound= function(){
            alert( sound );
        }
        
        return cat;
    }
}
 
var cat = Cat.createNew();
cat.makeSound();
cat.eat();

In fact, inheritance in JavaScript is more like 'we turn an Animal into a Cat'.

Protected Properties

Although a protected property is inaccessible from the outside, all objects that are part of the same family tree may share the protected components. To implement protected properties we create a new object called 'protect' that contains the shared stuff. I like to call this shared object, a bundle. If a bundle is passed to the 'Animal' constructor, the constructor will add new items to the bundle, if not it simply creates a new bundle. If the 'Cat' wishes to make use of the protected bundle, it simply passes the shared object to 'Animal':

Animal = {
    createNew: function( bundle ) {
     
        var animal = {};
     
        var protect = bundle || {};
     
        protect.sound = 'growl';
     
        protect.makeSound = function(){ 
            alert( protect.sound ); 
        }
        
        return animal;
    }
}
 
Cat = {
    createNew: function() {
        
        var protect = {};
        
        var cat = Animal.createNew( protect ); 
        
        protect.sound = 'meow!';
        
        cat.meow = function(){ protect.makeSound(); };
        
        return cat;
    }
}
 
var cat = Cat.createNew();
cat.meow();

Now, with these tools you can build object oriented designs. You can useencapsulation, inheritance and composition. Design patterns like singletons, factories and visitors are also easy to implement this way. Note that we did not use a lot of confusing JS stuff: no this, no new, no prototype. Therefore this approach is not only easy, minimalist and elegant but it also works in the most ancient and exotic browsers.



back to homepage
posted @ 2012-07-10 17:59  YutLee  阅读(125)  评论(0)    收藏  举报