Friday 2 March 2012

Inheritance in JavaScript

         JavaScript is a funny language. For a few weeks I have been reading the book "JavaScript : The Good Parts" by Douglas Crockford. This is the first time I've tried to look at javascript as more than a language just for browser. Syntactically javascript is pretty much like C, but it has much more in common with Lisp than with C. For someone familiar with languages like C and Java, it can be pretty weird at first reading.        
         One of those weird/funny things is that JavaScript has Class-free objects. There is no class hierarchy, objects inherit from objects. JavaScript provides prototypical inheritance. Every function object in javascript has prototype property whose value is an object containing a constructor property whose value is the function object.
          this.prototype = {constructor : this};
          The language attempts to provide pseudoclassical inheritance so that programmers used to the classical style don't feel bad. But in that attempt, the language has become even more complex and messy. Here is an example from the book :


        //define constructor for the pseudoclass
         var Mammal = function (name) {
                   this.name = name;
         };
         //augment its prototype
         Mammal.prototype.get_name = function ( ) {
                   return this.name;
         };
         Mammal.prototype.says = function ( ) {
                 return this.saying || '';
         };
         //we can make an instance:
         var myMammal = new Mammal('Herb the Mammal');
         var name = myMammal.get_name( ); // 'Herb the Mammal'


         //make another pseudoclass that inherits from Mammal
         var Cat = function (name) {
                this.name = name;
                this.saying = 'meow';
         };
         // Replace Cat.prototype with a new instance of Mammal
         Cat.prototype = new Mammal( );

        // Augment the new prototype with get_name method.
        Cat.prototype.get_name = function ( ) {
                return this.says( ) + ' ' + this.name +
                      ' ' + this.says( );
        };


        var myCat = new Cat('Henrietta');
        var says = myCat.says( ); // 'meow'
        var purr = myCat.purr(5); // 'r-r-r-r-r'
        var name = myCat.get_name( );
        //'meow Henrietta meow'


                So as we can see this code looks quite arbit and weird. So, instead of using this pseudoclassical complicated approach, we can try to use Prototypical inheritance as it should be used. The author has provided some good ways to get rid of this mess. One way is to define a method like following :

                Object.beget = function(o) {
                        var F = function () {};
                        F.prototype = o;
                        return new F();
                };

               Now we can just write :
              new_object = Object.beget(old_object);


       And then we can make changes to the new object without affecting the old one. This is called differential inheritance.


        In all these techniques, there is something quite important that  we haven't yet paid attention to. That is the idea of encapsulation. Here we use closures fundaes. Let's revisit the cat and mammal example : 
          




       var mammal = function (spec) {
            var that = {};
            that.get_name = function ( ) {
                return spec.name;
            };

            that.says = function ( ) {
                return spec.saying || '';
            };
            return that;
       };
       var myMammal = mammal({name: 'Herb'});

       var cat = function (spec) {
           spec.saying = spec.saying || 'meow';
           var that = mammal(spec);
           that.purr = function (n) {
               var i, s = '';
               for (i = 0; i < n; i += 1) {
                   if (s) {
                       s += '-';
                   }
                   s += 'r';
               }
               return s;
           };
           that.get_name = function ( ) {
               return that.says( ) + ' ' + spec.name +
                    ' ' + that.says( );
            }
            return that;
       };
       var myCat = cat({name: 'Henrietta'});


             I've given brief description here. If someone wants more details, they can visit
             http://www.crockford.com/javascript/inheritance.html
             http://javascript.crockford.com/prototypal.html


1 comment:

  1. In my course of "not regular basis" reading of the Crockford, I found him jabbering about the objecting the pseudoclassical approach in his following words=

    "There is no privacy; all properties are public. There is no access to super methods...Even worse, there is a serious hazard with the use of constructor functions. If you forget to use the new prefix when calling a constructor function, then this will not be bound to a new object...There is no compile warning, and there is no runtime warning."


    Whether functional or pseudoclassical, hazards do exist in any sort of scripting which is non-trivial.

    Over the real short time my really short experience has pointed me to see following more "hazard-prone" points of functional pattern.

    1)Instances of types take up more memory

    2)Types cannot be tested using instanceof

    3)Naming newly created objects is awkward

    4)Results in an extra level of indentation, to name a few.

    Functional approach may be great for purposes, but when we talk of regular stuff of making a little fancy browser jugglery, I would really appreciate pseudoclassical which is quite close to our good old OOP, rather than functional thing.

    ReplyDelete