Nov 27, 2008

jQuery mini

I am fasinated by the jQuery library. I do so much more with so less code. Here I try to mimic jQuery several features: chain operation, extentsion, constructor.

chain operation is pretty easy to mimic. We just need to add "return this;" to the end of a method. Here is my first try

var jQuery = function() {
}
jQuery.prototype =
{
    version: "1.0",
    showVersion: function() {
        alert(this.version);
        return this;
    },
    work: function() {
        alert("work");
        return this;
    }

}
var j = new jQuery();
j.showVersion().work();
        
    

Extension is also simple. We just need to decorate its prototype like this

var jQuery = function() {

}
jQuery.prototype =
{
    version: "1.0",
    showVersion: function() {
        alert(this.version);
        return this;
    },
    work: function() {
        alert("work");
        return this;
    }
}
var j = new jQuery();
j.showVersion().work();
jQuery.prototype.sleep = function() {
    alert("sleep");
    return this;
}
j.sleep();
    
    

The constructor is very tricky. First I need to create a jQuery object without "new" keyword. Can we just do this like the following?

    var jQuery = function() {
        return jQuery.prototype;
    }

    jQuery.prototype =
    {
        version: "1.0",
        showVersion: function() {
            alert(this.version);
            return this;
        },
        work: function() {
            alert("work");
            return this;
        }

    }

    var j = new jQuery();
    j.showVersion().work(); //chain works

    jQuery.prototype.sleep = function() {
        alert("sleep");
        return this;
    }

    j.sleep(); //extension works
    
    j.version = "2.0";
    j.showVersion();
    jQuery().showVersion(); //we can create a jQuery object without new, but it show 2.0, it is a singleton.
    

It works. But seemly, there is a serious bug, that it returns a singleton. So I want to write the following naive code.

var jQuery = function() {
    return new jQuery.prototype; 
    //jQuery.prototype is not a constructor
    //return new jQuery.prototype();  
    //jQuery.prototype is not a constructor
    
}    
    

It says, jQuery.prototype is not a constructor. so we need a constructor function to do the job.

    var jQuery = function() {
        return new jQueryConstructor();
    }

    jQuery.prototype =
    {
        version: "1.0",
        showVersion: function() {
            alert(this.version);
            return this;
        },
        work: function() {
            alert("work");
            return this;
        }

    }

    function jQueryConstructor() {
    };

    jQueryConstructor.prototype = jQuery.prototype;
    //end of library
    
    //following is test code.


    var j = new jQuery();
    j.showVersion().work(); //chain works

    jQuery.prototype.sleep = function() {
        alert("sleep");
        return this;
    }

    j.sleep(); //extension works

    j.version = "2.0";
    j.showVersion();
    jQuery().showVersion(); //it shows 1.0, it is not a singleton
    

Now everything works. But what if user change the jQuery.prototype accidentally. let's add a reference jQuery.fn = jQuery.prototype, so that user focus on jQuery.fn, and let jQuery.prototype off attention. So I change the code like the following . Please note that the "return new jQueryConstructor()", you must use "new". Without "new", the prototype "jQueryConstructor.prototype = jQuery.fn" has no use. Here is an artical Create Advanced Web Applications With Object-Oriented Techniques more about the "new".


var jQuery = function() {
    return new jQueryConstructor();
}

jQuery.fn = jQuery.prototype =
{
    version: "1.0",
    showVersion: function() {
        alert(this.version);
        return this;
    },
    work: function() {
        alert("work");
        return this;
    }

}

function jQueryConstructor() {
};

//jQueryConstructor.prototype = jQuery.prototype;
jQueryConstructor.prototype = jQuery.fn;

var j = new jQuery();
j.showVersion().work(); //chain works

jQuery.fn.sleep = function() {
    alert("sleep");
    return this;
}

j.sleep(); //extension works

j.version = "2.0";
j.showVersion();
jQuery().showVersion(); //it shows 1.0, it is not a singleton 
   
    

Can we make it more simple, yes, let's move the jQueryConstructor into the prototype.

var jQuery = function() {
    return new jQuery.fn.init();
}

jQuery.fn = jQuery.prototype =
{
    //nested init constructor inside 
    //is just a hack
    init: function() {},
    version: "1.0",
    showVersion: function() {
        alert(this.version);
        return this;
    },
    work: function() {
        alert("work");
        return this;
    }

}
//if we do not nested init inside
//it would be init.prototype = jQuery.prototype
jQuery.fn.init.prototype = jQuery.fn;

var j = new jQuery();
j.showVersion().work(); //chain works

jQuery.fn.sleep = function() {
    alert("sleep");
    return this;
}

j.sleep(); //extension works

j.version = "2.0";
j.showVersion();
jQuery().showVersion(); //it shows 1.0, it is not a singleton

    

Done. I have skeleton of jQuery.