这段时间在学习研究 jQuery 源码。随着 jQuery 的发展,研究和分析 jQuery 源码的人也越来越多,学习资料比前几年好找了不少,有很多非常不错的资源,例如高云的 jQuery1.6.1源码分析系列。这些教程对 jQuery 的内部原理和实现方式分析得非常细致,对学习和理解 jQuery 有很大的帮助。
但是个人认为,很多教程更侧重于具体代码的分析,对于 jQuery 的整体结构介绍得相对较少。所以本文试图从整体上简单分析一下 jQuery 的内部实现。
大家知道,jQuery 常见的调用方式有两种。一种是通过传递参数创建 jQuery 对象,例如通过 $("h1") 选择所有的 h1 元素;另一种则是直接调用 jQuery 自身提供的方法,例如通过 $.ajax() 进行 Ajax 操作。
那么,这两种方式到底有什么不同?
用 typeof 检测 $('h1') 和 $.ajax,类型分别为 object 和 function。稍微学过 jQuery 的都知道,前者返回的是一个 jQuery 对象。那么 jQuery 对象到底是什么?它和 jQuery 又是什么关系?
我们可以通过下面的方法查看 jQuery 对象中的属性:
for(var i in $("")){
document.write(i + " ::: " + $("")[i] + "<br>");
}可以看到,一个 jQuery 对象拥有很多属性和方法。通过 console 输入 $("*") 也可以看到,其中大量方法实际上是通过 jQuery 的原型继承而来的。
从整体上看,jQuery 对象大致可以理解成这样一个对象:

因此我们可以推测,jQuery 的实现可能类似下面这样:
function jQuery(){
this[0] = "Some DOM Element";
this[1] = "Some DOM Element";
this[2] = "Some DOM Element";
this.length = 3;
this.prevObject = "Some Object";
this.context = "Some Object";
this.selector = "Some selector";
}
jQuery.prototype = {
get: function(){},
each: function(){},
//......
}这些代码通过 new 操作符就可以创建出拥有上述属性的 jQuery 对象。但是实际上,我们调用 jQuery 创建对象时并没有使用 new 操作符,这是如何实现的呢?来看一下 jQuery 的实现:
var jQuery = function(selector, context) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init(selector, context, rootjQuery);
}
jQuery.fn = jQuery.prototype = {
jquery: core_version,
init: function(selector, context){
// some code
return this;
}
// some code there
//......
}
jQuery.fn.init.prototype = jQuery.fn;这里有几个地方做得非常巧妙。
第一点是 jQuery 本身实际上只是一个函数,我们调用:
$("h1");实际上就是调用:
jQuery("h1");然后由 jQuery 函数内部执行:
newjQuery.fn.init(selector, context, rootjQuery);
也就是说,new 并没有消失,而是被 jQuery 封装在了内部,因此我们在使用 jQuery 时不需要手动使用 new,直接通过 $() 就可以创建一个 jQuery 对象。
第二点是 init 创建出来的对象如何继承 jQuery 原型上的方法。
我们知道,通过 new jQuery.fn.init() 创建出来的实例,默认继承的是 jQuery.fn.init.prototype。而 jQuery 通过:
jQuery.fn.init.prototype =jQuery.fn;
把 init 的原型指向 jQuery.fn。由于:
jQuery.fn ===jQuery.prototype
因此 init 创建出来的 jQuery 对象就能够继承 jQuery.fn 上定义的属性和方法。
到这里,一个 jQuery 的基本原型结构就逐渐清晰了。
这里实际上可以简单理解为两类对象:一个是 jQuery 本身,它是一个函数,同时拥有自己的属性和方法;另外一个是通过 $() 创建出来的 jQuery 对象,它和普通 JavaScript 对象一样,只是继承了 jQuery 提供的大量方法。
如下关系图:
![[ jQuery结构 ]](/dm-content/uploads/adznqbkx.png)
可以看到,jQuery 本身和 jQuery.prototype 都有各自的属性和方法,两者的调用方式也不一样。例如:
$.ajax()
属于 jQuery 自身的方法,而:
$("h1").each()则是 jQuery 对象继承自 jQuery.fn 的方法。
这两个对象都有一个 extend 方法,都是用来扩展自身的属性和方法。在 jQuery 内部,这两个 extend 最终使用的核心逻辑是相同的,只是扩展的目标不同。关于 extend 的具体实现,后面再结合源码做进一步分析。
评论0
欢迎分享你的看法,也欢迎补充不同的实践经验。