Nov 24, 2008

Javascript Inheritance

In the world of javascript, everything is object. There is no class. There are two kind of object, one can not create object, let's call it non-constructor object, and one can create object constructor object. Here when I mention constructor, it mean consturctor object. When I mention object, it means all objects including both kinds.

First let's talk about constructor. All object has a constructor, a constructor has a constructor. So can we traverse like this o.constructor.constructor.constructor.... and on and on? Yes, but the final constructor is function Function { [native code] }. Function constructor is itself.

    function Dog() { };
    var d = new Dog(); 
    alert(d.constructor); //function Dog() {} 
    alert(d.constructor.constructor); //function Function { [native code] }; 
    alert(d.constructor.constructor.constructor); //function Function { [native code] }; 
    alert(d.constructor.constructor.constructor.constructor); //function Function{ [native code] };

Inheritance implementation in javascript is different from "classical" world. Because there is no such thing "class" in javascript. It use prototype to simulate inheritance. We should know this.

  • A constructor has prototype.
  • A non-constructor object has no prototype
  • A prototype is non-constructor object so it has no prototype

The first user defined constructor is the constructor has no parent. Let call it first level constructor. The prototype of first level consturctor is [object Object], which is non-constructor object, but it has contructor which is the first level constructor itself, which means the prototype is of the constructor created by the constructor. So we can say the first level constructor's prototype is created by the constructor.

    function Dog() { this.name = "fred"; }
    var d = new Dog(); 
    alert(Dog.prototype); //[object Object] 
    alert(Dog.prototype.constructor); //function Dog() {} 

Now want to simulate the inheritance. So we create second level constructor like this.

    function Puppy() { this.age = 18; }
    Puppy.prototype = new Dog(); // its prototype is a dog
    alert(Puppy.constructor.prototype); //function (){}, but it is actually a dog
    
    var p = new Puppy(); 
    alert(p.name); //it can assess member "name" of it is prototype
    alert(p.age);
    
    var p2 = new Puppy(); 
    p2.name = "jeff"; 
    alert(p2.name); //jeff
    alert(p.name); 

Please note that the object created by the second level constructor can assess the members of of its prototype, which is created by first level constructor. And These members are shared at the beginning, but if it is changed, then a private copy is created at the second level. But the shared copy in the prototype is not changed. In this way, it is memory efficent.

Above we use the pseduoclassical to simulate the inheritance, there is another way to do that, closure. Below is the demo code.

    function Dog() {
        return { name: "fred" }
    }

    function Puppy() {
        var d = Dog();
        d.age = 18;
        return d;
    }

    var p = Puppy();
    alert(p.name);
    alert(p.age);

Closue is much clean but it is not easy to understand for new javascript developer particularly those from the classical world. Please note that we don't need to use the "new" keyword to to cal the constructor, but we still can. If use the pesudoclassical way, we need to use new, because the constructor does not return an object explicitly