导语:
如何提高编程能力,这几乎是媒体们采访一些编程大神的必问的问题,大部分大神都会提到多读源码,本人觉得确实有道理,因为源码中的一些方法或者思想确实能够让我们跳出自己的思维模式,拓展我们的编程思维。下面我们就来浅析jQuery源码部分知识。
jQuery源码差不多有上万行代码了,下面是其各部分的架构(在博客园上拷贝的一张图):
Paste_Image.png
**(一)、总体框架 **
首先我们来总体看下jquery的代码:
(function(window, undefined) { // jQuery 代码})(window);
通过上面我们很容易看到jQuery的实现方法都被包含在立即执行函数中了,这样我们在引用jQuery时就马上执行了,并且jQuery里面的变量不会污染外界的变量。(对于单独暴露出的$及jQuery这两个参数其也做了防冲突处理这个我们在下一点将会谈到)。其中我们还看到在这个立即执行函数中在外界传了一个“window”参数,其本身有两个形参---一个是window另一个是undefined。那么为什么形参中有undefined的呢?这其实是针对更早期的环境当中(pre-ES5,eg. Internet Explorer 8)的undefined,因为在那个环境中undefined可以被改写,这样做确保jQuery里面的undefined确实是undefined而不是外面所更改的undefined,保证代码的正常运行。
举个简单的例子我们就能理解下:
(function exmple(one,two){ console.log(one); console.log(two); }("a"));
上面的代码执行后,会打印一个是“a”,另一个是undefined,
Paste_Image.png
因为我传实参是并没有给two这个形参传递实参,因此打印它时便是undefined。
**(二)、防冲突处理 **
上面我们提到了单独暴露出的$及jQuery这两个参数jQuery库也做了防冲突处理,那么它是怎么做防冲突处理的呢?请看下面:
(function(window, undefined) { var ... _jQuery = window.jQuery, _$ = window.$,//以上两行代码的意思是将window环境中的jQuery及$赋给了_jQuery及_$....//中间的jQuery代码jQuery.extend({ ..... noConflict: function( deep ) { if ( window.$ === jQuery ) { window.$ = _$;//以上语句进行判断全局下的$是否与 jQuery变量相等,如果相等,则把$还原回 jQuery运行之前的//状态(通过存储在内部的变量_$赋给window.$),此时 jQuery的别名$失效 } if ( deep && window.jQuery === jQuery ) { window.jQuery = _jQuery; }//以上语句表示的意思是当开启深度冲突处理(deep为true)且jQuery变量和全局的jQuery相等时,//将jQuery还原回jQuery运行之前的状态(通过存储在内部的变量_jQuery赋给window.jQuery),//此时在全局中使用 jQuery库的 jQuery 失效 return jQuery;// 这里将返回jQuery 库的jQuery构造函数 new jQuery.fn.init() } ..... }) }(window)
**(三)、无new构造对象 **
我们都知道我们在使用jQuery时,会首先使用$('xxx')再在其后面使用一些方法比如$('div').css("width","50px")
里的css。那么为什么在外界不需要使用new的方式构建jQuery对象呢?还有.text()、.append()、$.ajax()、$.trim()等等这些方法是怎么绑定的呢?接下来我们来分析下。
首先我们来分析下jQuery获取元素时为何不需要使用new的方式创建对象而直接使用$()或者jQuery()就可以构造对象。
(function(window, undefined) { var // ... jQuery = function(selector, context) { return new jQuery.fn.init(selector, context, rootjQuery); // 这里很巧妙,实例化方法 jQuery() 实际上是调用了jQuery.fn.init 也可称作jQuery 对象构造器 }, jQuery.fn = jQuery.prototype = { //赋给 jQuery原型里面的一些方法 init: function(selector, context, rootjQuery) { // ... } } jQuery.fn.init.prototype = jQuery.fn;//这一句也很巧妙:将 jQuery的原型赋给了 jQuery.fn.init的原型,这样新构造的 jQuery对象也可以//使用jQuery的原型的一些方法与属性了 })(window);
其次我们来分析下上述第二个问题.text()、.append()、$.ajax()、$.trim()等是怎么挂载的。
a、我们先来看看jQuery中的.text()、.append()、.prepend()等实例方法
Paste_Image.png
我们可以看到.text()、.append()、.prepend()等实例方法都添加至jQuery原型上去了(通过extend 方法扩展);
b、接下来我们来看看jQuery中的$.each()、$.trim()等静态方法(也称工具方法)是怎么挂载的。
Paste_Image.png
通过查看源码我们发现,$.each()、$.trim()等静态方法都添加至jQuery本身上了,(也是通过extend 方法扩展的)这样我们就可以直接使用$.each('')、$.trim('')等方法了;
c、关于extend 方法是怎么实现的我们后面再详细做介绍;
**(四)、jQuery链式调用 **
我们都知道在jQuery中我们可以采用$('div').addClass("wrap").css("height","50px")
这样的方式书写也就是在选择一个元素后可以连续的在后面加方法,这其实就是链式调用。我们可以看看jQuery的源码:
Paste_Image.png
原来jQuery其实是通过返回this来实现链式调用的。以前我在这篇文章中也提到过链式调用,这里再贴出来那个简单的例子:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>jQuery</title> <style type="text/css"> </style></script></head><body> <script> var Dog=function (name,age){ this.name=name; this.age=age; }; Dog.prototype={ getName:function(){ console.log(this.name); return this }, getAge:function(){ console.log(this.age); return this } } ; Dogs=function(name,age){ return new Dog(name,age); } ; Dogs("goutou",3).getName().getAge(); </script></body></html>
执行结果
**(五)、 钩子机制(hook) **
关于jQuery中的钩子机制可见这篇文章的钩子机制(hook)部分,写的很精彩。我就不在这赘言了。当然那篇博客里写的其他部分同样不错,推荐大家看看。
好了,今天先写到这里,后面有时间会继续浅析jQuery源码,jQuery里面的方法和思想确实令我们佩服并值得我们学习。
ps:其实还有多看看优秀库的代码,比如短小精悍的 underscore 也相当有帮助。
作者:该帐号已被查封
链接:https://www.jianshu.com/p/6881d6eb66cb