Difference of the value, prototype and property
OK! First of all this question comes from a man who digs too deep (and posibly get lost) in the jQuery universe.
In my reserch I discovered the jquery's main pattern is something like this (If needed correction is wellcomed):
(function (window, undefined) {
jQuery = function (arg) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init(arg);
},
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function (selector, context, rootjQuery) {
// get the selected DOM el.
// and returns an array
},
method: function () {
doSomeThing();
return this;
},
method2: function () {
doSomeThing();
return this;,
method3: function () {
doSomeThing();
return this;
};
jQuery.fn.init.prototype = jQuery.fn;
jQuery.extend = jQuery.fn.extend = function () {
//defines the extend method
};
// extends the jQuery function and adds some static methods
jQuery.extend({
method: function () {}
})
})
When $
initiates the jQuery.prototype.init
initiates and returns an array of elements. But i could not understand how it adds the jQuery method like .css
or .hide
,etc. to this array.
I get the static methods. But could not get how it returns and array of elements with all those methods.
I don't like that pattern either. They have an init
function, which is the constructor of all jQuery instances - the jQuery
function itself is just a wrapper around that object creation with new
:
function jQuery(…) { return new init(…); }
Then, they add the methods of those instances to the init.prototype
object. This object is exposed as an interface at jQuery.fn
. Also, they set the prototype
property of the jQuery function to that object - for those who don't use the fn
property. Now you have
jQuery.prototype = jQuery.fn = […]init.prototype
But they also do two [weird] things:
- overwriting the
constructor
property of the prototype object, setting it to thejQuery
function - exposing the
init
function onjQuery.fn
- its own prototype. This might allow Extending $.fn.init function, but is very confusing
I think they need/want to do all this to be fool-proof, but their code is a mess - starting with that object literal and assigning the init prototype things afterwards.
It’s easier to digest if you think of the API as an external collection of methods, and the jQuery function as the wrapper.
It’s basically constructed like this:
function a() { return new b();}
a.prototype.method = function() { return this; }
function b() {}
b.prototype = a.prototype;
Except that a
is jQuery
and b
is jQuery.prototype.init
.
I’m sure Resig had his reasons for placing the api constructor in the init prototype, but I can’t see them. A couple of more strangeness besides the ones Bergi mentioned:
1) The patterns requires a reference copy from jQuery.fn.init.prototype
to jQuery.prototype
, wich allows a weird endless loop:
var $body = new $.fn.init.prototype.init.prototype.init.prototype.init('body');
2) Every jQuery collection is actually an instance of jQuery.fn.init
, but since they reference the same prototype object, it tricks us to "think" that the collection is an instance of jQuery
. You can do the same sorcery like this:
function a(){}
function b(){}
a.prototype = b.prototype;
console.log( new b instanceof a); // true
console.log( new a instanceof b); // true
Sidenote: I have personally used the following constructor pattern with similar results without the weirdness:
var a = function(arg) {
if (!(this instanceof a)) {
return new a(arg);
}
};
a.prototype.method = function(){ return this; };