blog-md
blog-md copied to clipboard
# jQuery源码 构造器及 extend方法疑云
昨天在byr 论坛上看到一个帖子,关于 jQuery 初始化以及原型和 extend 方法的疑问,一直以来使用 jQuery 觉得方便的紧,那么是如何做到的呢?带着这些疑问对 jQuery 源码进行一些分析整理和小测,记录下~
jQuery构造器
jQuery使用非常方便,其中一个原因就是我们在调用的时候并不需要使用关键字new来创造一个jQeury对象,直接使用jQuery(“#id”)
或$(“.class”)
就可轻松得到一个新的jQuery对象。原因就是jQuery使用工厂方法,利用构造器创造一个新的jQuery对象并返回,省去了用户的new操作。
来看一下 jQuery 的构造代码:
(function (window, document, undefined) {
var w = window,
doc = document;
// 每一个 jQuery 实例都是init 实例的增强
var jQuery = function (selector) {
return new jQuery.fn.init(selector);
}
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
length: 0,
splice: [].splice,
selector: '',
init: function (selector) {//dom选择的一些判断
}
}
// 原型合一
jQuery.fn.init.prototype = jQuery.prototype;
jQuery.ajax = function () { //直接挂载方法 可$.ajax调用
console.log(this);
}
window.$ = jQuery;
})(window, document);
返回一个jQuery.fn.init()
jQuery.fn其实就是 jQuery.prototype, 除去压缩代码时会减小库的容量,其实这个更多的是历史原因。。。最初的jQuery 实例的成员方法都写在了 jQuery.fn 上,后来索性直接将 jQuery.fn = jQuery.prototype, 免去了每个实例都要初始化公有方法的尴尬。
通过源码我们看到,每一个 jQuery 实例都是 init 函数的实例,jQuery 本身可以看做一个类,这个类上有静态方法比如$.ajax()
,也有构造器构造出来的实例方法如$('div').addClass('active')
。
init 这个构造函数本身就是 jQuery 的原型中的方法,init 里面的属性和方法是每一个实例初始化的私有方法,实例拥有独立的命名空间,私有方法不会冲突。
关于 jQuery.extend()和 jQuery.fn.extend()
新版的 jQuery 当传入不止一个参数时,$.extend()
会把后者拓展到第一个对象中,还可以设置是否深拷贝,这些我们不谈,来看看两个 extend()的区别和关联。
看下源码:
// 两个方法其实是相同的
jQuery.extend = jQuery.fn.extend = function() {
//目标对象
var target = arguments[0] || {},
...
if (length === i) {
//target = this;这句代码是整个extend函数的核心
//在这里目标对象被更改,这里的this指向调用者
//在 $.extend()方式中表示jQuery对象本身
//在 $.fn.extend()方式中表示jQuery函数所构造的对象(即jQuery类的实例)
target = this;
...
}
};
看到以上几句其实就很清楚了,两个 extend方法完全相同,只不过内部的指向this 有区别。
(只传入一个参数的时候)导致前者挂载的方法成了$.ajax()
这样的 jQ 的静态私有方法,后者挂载到了 jQ.fn 上,也就是 jQ的原型,而原型方法是被所有实例共享的,但构造函数 jQuery 无法直接调用自身的 prototype 方法(类似的$.fn.extend()
后,$('div').sayHello()
是可以的,$.sayHello()
报错,$.prototype.sayHello()这样就可以了)。
总结
通过了解 jQuery 的构造函数,以及两个 extend()方法的区别联系,对 jQuery 又有了新的认识~