如果我们要重头设计一个jQuery,用户要创建一个jQuery对象,怎么创建?
最直接的方法:
// 执行匿名函数
(function() {
var jQuery = function(selector) {
this.selector = selector;
}
window.jQuery = jQuery;
})();
// 创建jQueyr对象
var obj = new jQuery("div");
但很讨厌,每次都要写一个new关键字,但如何避免每次都要写一个new呢?
很自然的,下面是一种解决方法:
(function() {
var jQuery, init;
init = function(selector) {
this.selector= selector;
}
jQuery = function(selector) {
return new init(selector);
}
window.jQuery = jQuery;
// 除了向外暴露jQuery全局变量外,还需要暴露init全局变量,否则无法扩展jQuery对象
window.init = init;
})();
// ========测试代码=============
var b = jQuery("div");
console.log(b);
// 当需要扩展jQuery对象时,需要修改init的prototype。
// jQuery.prototype.attr = function() { alert("implementation of attr function"); }
// 上述是错误的写法,修改jQuery的prototype并不会扩展new init()创建的对象
init.prototype.attr = function() { alert("implementation of attr function"); };
b.attr();
以上的代码是没有什么大问题的,但如果这样的话,当用户需要扩展jQuery对象的方法时(即jQuery插件),显然的是要暴露两个全局变量jQuery和init,否则用户无法扩展jQuery对象。
那么怎么做才能尽可能少的暴露全局变量呢?我们可以把jQuery的prototype指向init的prototype,这样,当扩展jQuery的prototype时,也意味着修改了init的prototype
(function() {
var jQuery, init;
init = function(selector) {
this.selector= selector;
}
jQuery = function(selector) {
return new init(selector);
}
// 把init的prototype指向jQuery的prototype,这样,当扩展jQuery的prototype时,也意味着修改了init的prototype
jQuery.prototype = init.prototype;
window.jQuery = jQuery;
})();
// ========测试代码=============
var b = jQuery("div");
console.log(b);
// 当需要扩展jQuery对象时,只需修改jQuery的prototype。
jQuery.prototype.attr = function() { alert("implementation of attr function"); };
b.attr();
上面的思路也大致是jQuery的设计思路,但jQuery更进一步,它认为init是jQuery对象的构造函数,也应该可以通过jQuery对象访问,因此jQuery里直接将init定义为jQuery.prototype中的一个成员。
jQuery中定义jQuery全局变量的源代码如下,特别需要注意其中的标注的代码:
(function( global, factory ) {
if ( typeof module === "object" && typeof module.exports === "object" ) {
// For CommonJS and CommonJS-like environments where a proper `window`
// is present, execute the factory and get jQuery.
// For environments that do not have a `window` with a `document`
// (such as Node.js), expose a factory as module.exports.
// This accentuates the need for the creation of a real `window`.
// e.g. var jQuery = require("jquery")(window);
// See ticket #14549 for more info.
module.exports = global.document ?
factory( global, true ) :
function( w ) {
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
return factory( w );
};
} else {
factory( global );
}
// Pass this if window is not defined yet
}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
var
version = "1.12.4",
// Define a local copy of jQuery
// 重点代码
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init( selector, context );
};
// 重点代码
jQuery.fn = jQuery.prototype = {
};
// A central reference to the root jQuery(document)
var rootjQuery,
// 重点代码
init = jQuery.fn.init = function( selector, context, root ) {
};
// Give the init function the jQuery prototype for later instantiation
// 重点代码
init.prototype = jQuery.fn;
// Expose jQuery and $ identifiers, even in
// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
// and CommonJS for browser emulators (#13566)
if ( !noGlobal ) {
window.jQuery = window.$ = jQuery;
}
return jQuery;
}));