要学习一个库首先的理清它整体架构:

1、jQuery源码大致架构如下:(基于 jQuery 1.11 版本,共计8829行源码)
(21,94)                定义了一些变量和函数jQuery=function(){}
(96,280)        给jQuery添加一些方法和属性,jQuery.fn=jQuery.prototype
(285,347)        extend:        jQuery的一些继承方法        更容易进行后续的扩展                                                
(349,817)        jQuery.extend(): 扩展一些工具方法
(877,2856)        Sizzle:复杂选择器的实现
(2880,3042) Callbacks:回调对象——》对函数的统一管理
(3043,3183)        Deferred:延迟对象——》对异步的统一管理
(3184,3295)        support:功能检测
(3308,3652)        data():数据缓存
(3653,3797)        queue():队列管理
(3083,4299)        attr(),prop() val() addClass():属性操作
(4300,5128)        on() trigger():事件的相关方法
(5140,6057)        DOM操作:添加 删除 获取 包装 筛选
(6058,6620)        css():针对样式的操作
(6621,7854) 提交的数据和Ajax()的操作:ajax() load() getJSON()
(7855,8584)        animate():运动的方法
(8585,8792)        offset:位置与尺寸的方法 
(8826)                对外提供jQuery对象接口:window.jQuery=window.$=jQuery;

贴出一位大牛司徒正美对jquery的剖析:(最近也在读他框架设计一书,深深折服,这里推荐下)

jQuery强在它专注于DOM操作的思路一开始就是对的,以后就是不断在兼容性,性能上进行改进。

  • ajax 数据交互
  • attributes 属性操作,共分className, 表单元素的value值,属性与特征四大块。
  • callbacks 函数列队
  • core 种子模块,命名空间,链式结构,domReady,多库共存。
  • css 样式操作,引入DE大神的两个伟大的hacks,基本上解决精确获取样式的问题。
  • data 数据存取。
  • deferred 异步列队(三合一版的函数列队)
  • dimensions 元素尺寸的设置读取(来自社区)。
  • effects 动画引擎
  • event 事件系统(基于DE大神的事件系统与社区的两个插件)
  • exports AMD系统(被RequireJS作者说服加几行代码支持其东东)
  • manipulation 节点的操作
  • offset 元素offsetTop(Left)的设置读取
  • queue 列队模块(deferred与data的合体)。
  • sizzle 从右到左进行解析的选择器引擎。
  • support 特征侦测
  • traversing 元素遍历。

2、使用jQuery时,我们经常会在原生DOM对象和jQuery对象之间产生困扰,为什么DOM对象没有这个方法,jQuery实例对象可以轻松实现某些功能。接下来看看jQuery实例对象是怎么创建。

(function( window, undefined ) {
var jQuery = (function() {
// 构建jQuery对象
var jQuery = function( selector, context ) {
return new jQuery.fn.init( selector, context, rootjQuery );
} // jQuery对象原型
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
    }
init = jQuery.fn.init = function( selector, context, rootjQuery ) {
// selector有以下7种分支情况:
// DOM元素
// body(优化)
// 字符串:HTML标签、HTML字符串、#id、选择器表达式
// 函数(作为ready回调函数)
// 最后返回伪数组
} init.prototype = jQuery.fn;// 合并内容到第一个参数中,后续大部分功能都通过该函数扩展
// 通过jQuery.fn.extend扩展的函数,大部分都会调用通过jQuery.extend扩展的同名函数
jQuery.extend = jQuery.fn.extend = function() {}; // 在jQuery上扩展静态方法
jQuery.extend({
// ready bindReady
// isPlainObject isEmptyObject
// parseJSON parseXML
// globalEval
// each makeArray inArray merge grep map
// proxy
// access
// uaMatch
// sub
// browser
}); // 到这里,jQuery对象构造完成,后边的代码都是对jQuery或jQuery对象的扩展
return jQuery; })(); window.jQuery = window.$ = jQuery;
})(window);

从这段代码可以看出:

(1)、 jQuery对象不是通过 new jQuery 创建的,而是通过 new jQuery.fn.init 创建的。

(2)、jQuery对象就是jQuery.fn.init对象( new jQuery.fn.init( selector, context, rootjQuery );)

(3)、 jQuery.fn指向了jquery的原型(实际上是用属性名fn代替prototype,为后面编程使用方便,要不然多次使用prototype会很绕):

jquery 原型定义了jquery对象初始化init的一系列方法。

(4)、接下来,定义在jQuery.fn.init,后面又加上这句init.prototype = jQuery.fn :即jQuery.fn.init的原型也指向了jQuery的原型,有点绕哈,合并下:

jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype,实例说明一下,

这说明了我们使用new jQuery.fn.init()创建的实例对象的原型就是jQuery的原型,这样你就可以在jquery实例对象就可以直接调用jQuery这个类原型上挂载的方法了。

(5)、检验一个库的好坏是看这个库的扩展性和兼容性。jQuery.extend = jQuery.fn.extend = function() {}是jQuery对外提供扩展的方法

jQuery之所以如此强大,就是它能很容易实现类方法(虽然javascript没有类的概念,但这里还是用类来理解下,希望不要误解到读者)和实例方法扩展。

下面分析下jQuery实例对象这么创建的。

init = jQuery.fn.init = function( selector, context ) {
var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false)
if ( !selector ) {
return this;
} // Handle HTML strings
if ( typeof selector === "string" ) {
if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
// Assume that strings that start and end with <> are HTML and skip the regex check
match = [ null, selector, null ]; } else {
match = rquickExpr.exec( selector );
} // Match html or make sure no context is specified for #id
if ( match && (match[1] || !context) ) { // HANDLE: $(html) -> $(array)
if ( match[1] ) {
context = context instanceof jQuery ? context[0] : context; // scripts is true for back-compat
// Intentionally let the error be thrown if parseHTML is not present
jQuery.merge( this, jQuery.parseHTML(
match[1],
context && context.nodeType ? context.ownerDocument || context : document,
true
) ); // HANDLE: $(html, props)
if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
for ( match in context ) {
// Properties of context are called as methods if possible
if ( jQuery.isFunction( this[ match ] ) ) {
this[ match ]( context[ match ] ); // ...and otherwise set as attributes
} else {
this.attr( match, context[ match ] );
}
}
} return this; // HANDLE: $(#id)
} else {
elem = document.getElementById( match[2] ); // Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if ( elem && elem.parentNode ) {
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem.id !== match[2] ) {
return rootjQuery.find( selector );
} // Otherwise, we inject the element directly into the jQuery object
this.length = 1;
this[0] = elem;
} this.context = document;
this.selector = selector;
return this;
} // HANDLE: $(expr, $(...))
} else if ( !context || context.jquery ) {
return ( context || rootjQuery ).find( selector ); // HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
return this.constructor( context ).find( selector );
} // HANDLE: $(DOMElement)
} else if ( selector.nodeType ) {
this.context = this[0] = selector;
this.length = 1;
return this; // HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) ) {
return typeof rootjQuery.ready !== "undefined" ?
rootjQuery.ready( selector ) :
// Execute immediately if ready is not present
selector( jQuery );
} if ( selector.selector !== undefined ) {
this.selector = selector.selector;
this.context = selector.context;
} return jQuery.makeArray( selector, this );
};

分析上面代码:

1、jQuery.fn.init的功能是对传进来的selector参数进行分析,进行各种不同的处理,然后创建jQuery实例对象。

2、arguments:选择器和使用的上下文环境(比较生涩:举个例子,比如在地图上找深圳这个地方,选择器好比深圳这个地名,熟悉的人可能一下子用经纬度定位了,东经­113°46'~114°37',北纬22°27'~22°52'(参数context都不用传了^^),对歪国仁来说,可能第一眼的先定位到我们的公鸡版图:也就是context:china,这样搜索速度就加快了。对国人(闭眼也能找着祖国哈^^)来说直接找广东—靠海—靠香港:也就是context:广东)。

3、接下来看看怎么分析selector参数类型以及相应的做了什么处理。

问题来了,看到这个正则这么长有没有要晕,要吐的感觉,

rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,这里写的什么。呃这个暂时不解释,后面专门奉上一个正则表达专题供大家解读…………。

(1)  $(""), $(null), $(undefined), $(false);这几种情况直接返回 this,这里this是什么呢? 注意不是JQuery本身,而是jQuery.fn.init

(2)、字符串类型

a:HTML标签形式的——$(<>):通过document.createElement 创建节点。类似 $("<div></div>")、$("<div>aaa</div><div>aaa</div>") 两种情况;

而其他情况如:$(“</div>111”)、$("#id")、$(".class")、$("div")、$("#id .class div"),其中requickExpr匹配的是$(“</div>111”)、$("#id")。如果传入的是$(“</div>111”),则match=["</div>111","</div>",null];如果传入的是$("#id"),则match=["#id",null,“id”]。

b:HTML字符串——$(html) -> $(array):创建DOM并扩充到jQuery对象

c:#id——$(#id):直接通过document.getElementById获取dom节点。

d:选择器表达式——$(expr, $(...)):

(3)、DOMElement:直接返回将该对象,只是修改了context属性和length属性。

(4)、function:用过jquery基本上都会初始化代码写到 $(function(){……})里,就是DOMLoaded 加载完毕之后回调执行改匿名函数。

(5)、slector.slector = undefined 这个眼戳暂时没看懂,有谁看懂提下谢谢 。

上面涉及到的函数这里先提前脑补一下:$.parseHTML 、$.merge、$.isPlainObject(

$.parseHTML :将字符串转换为存储DOM节点的数组 。第一个参数为传入的字符串,第二个为指定的根节点,第三个是boolean值 (“<script></script>”是否可以转化为<script>标签),默认为false,不转换。

$.merge:合并两个数组,在jQuery内部不仅可以合并数组,也可以合并类数组。

$.isPlainObject():判断传入的参数是否是由 {}或new Object 创建的对象。

3、下面看看jQuery原型挂载了什么?

jQuery.fn = jQuery.prototype = {
// The current version of jQuery being used
jquery: version, constructor: jQuery, selector: "", length: 0, toArray: function() {
return slice.call( this );
}, get: function( num ) {
return num != null ?
( num < 0 ? this[ num + this.length ] : this[ num ] ) : slice.call( this );
}, pushStack: function( elems ) { var ret = jQuery.merge( this.constructor(), elems ); ret.prevObject = this;
ret.context = this.context; return ret;
}, each: function( callback, args ) {
return jQuery.each( this, callback, args );
}, map: function( callback ) {
return this.pushStack( jQuery.map(this, function( elem, i ) {
return callback.call( elem, i, elem );
}));
}, slice: function() {
return this.pushStack( slice.apply( this, arguments ) );
}, first: function() {
return this.eq( 0 );
}, last: function() {
return this.eq( -1 );
}, eq: function( i ) {
var len = this.length,
j = +i + ( i < 0 ? len : 0 );
return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
}, end: function() {
return this.prevObject || this.constructor(null);
}, push: push,
sort: deletedIds.sort,
splice: deletedIds.splice
};

1、先来看这三个属性:push、sort、splice 都是数组 Array()原型上挂载的方法。仅供jQuery内部使用(私有方法),与jQuery外部工具方法(对外可使用)不同

2、first、last、eq这几个方法都是都是又来获取数组某个元素的。

3、重点来了:pushStack在jQuery中使用频率特别高,它是干嘛的?只看这段代码。只能知道 它调用了 jQuery.merge,合并了类数组。

jQuery.extend = jQuery.fn.extend = function() {
var src, copyIsArray, copy, name, options, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false; if ( typeof target === "boolean" ) {
deep = target; target = arguments[ i ] || {};
i++;
} if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
} if ( i === length ) {
target = this;
i--;
} for ( ; i < length; i++ ) {
if ( (options = arguments[ i ]) != null ) {
for ( name in options ) {
src = target[ name ];
copy = options[ name ]; if ( target === copy ) {
continue;
} if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
if ( copyIsArray ) {
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : []; } else {
clone = src && jQuery.isPlainObject(src) ? src : {};
} target[ name ] = jQuery.extend( deep, clone, copy ); } else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
return target;
};

1、功能:jQuery.extend(object); 为扩展jQuery类本身.为类添加新的方法。jQuery.fn.extend(object);给jQuery实例对象添加方法。

2、结合$.extend使用方法来分析:

(1)、var newSrc=$.extend({},src1,src2,src3...)
(2)、var newSrc=$.extend(false,{},src1,src2,src3...)//嵌套子对象不拷贝
(3)、var newSrc=$.extend(true,{},src1,src2,src3...)

从arguments可以看出,extend是为了合并两个或更多对象的属性到第一个对象中。

3、再来分析代码:

看第一个逻辑意思是:如果第一个参数是布尔型的话,要实现扩展的对象target就是第二个参数,接下来再判断此时target是否对象或函数,都不是则默认扩展到{}。

接下来两个for循环,第一个循环遍历后续所有对象,第二个循环 实现将每个对象属性的copy到第一个对象。看似很简单,问题来了,target的属性与需要copy对象属性一样时,怎么处理?是直接覆盖/重写,还是合并?这就涉及到了浅拷贝与深拷贝,那浅拷贝与深拷贝又是什么。

(1)、js对象浅拷贝简单的赋值就是浅拷贝。因为对象和数组在赋值的时候都是引用传递。赋值的时候只是传递一个指针。也就是说遇到同名属性时直接覆盖/重写。

(2)、因为对象相对较为复杂,所以我们先来看对数组的深拷贝的问题

来看个例子,说明一下

extend(boolean,dest,src1,src2,src3...)

第一个参数boolean代表是否进行深度拷贝,其余参数和前面介绍的一致,什么叫深层拷贝,我们看一个例子:

var result=$.extend( true,  {},  
{ name: "John", location: {city: "Boston",county:"USA"} },
{ last: "Resig", location: {state: "MA",county:"China"} } );

我们可以看出src1中嵌套子对象location:{city:"Boston"},src2中也嵌套子对象location:{state:"MA"},第一个深度拷贝参数为true,那么合并后的结果就是:

result={name:"John",last:"Resig",location:{city:"Boston",state:"MA",county:"China"}} ////浅拷贝,对copy对象同名属性值类型为数组或对象的属性进行合并

也就是说它会将src中的嵌套子对象也进行合并,

而如果第一个参数boolean为false,我们看看合并的结果是什么,如下:

var result=$.extend( false, {},  
{ name: "John", location:{city: "Boston",county:"USA"} },
{ last: "Resig", location: {state: "MA",county:"China"} } );

那么合并后的结果就是:

  result={name:"John",last:"Resig",location:{state:"MA",county:"China"}}//浅拷贝,对copy对象同名属性值类型为数组或对象的属性直接覆盖/重写。

来看下深拷贝和前拷贝怎么实现的:

if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
if ( copyIsArray ) {
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : []; } else {
clone = src && jQuery.isPlainObject(src) ? src : {};
} target[ name ] = jQuery.extend( deep, clone, copy ); } else if ( copy !== undefined ) {
target[ name ] = copy;
}

(1)、浅拷贝:target[ name ] = copy;直接覆盖和容易理解。

(2)、深拷贝稍显复杂,首先判断是数组还是对象,在对嵌套子对象递归调用$.extend方法。

4、jQuery多库共存处理

$符号已经被很多库作为作为命名空间,因此不免会与别的库框架或者插件相冲突。

jQuery引入noConflict函数可以将变量$的控制权让给第一个实现它的那个库,确保jQuery不会与其他库的$对象发生冲突。在运行这个函数后,就只能使用jQuery变量访问jQuery对象。下面来看看代码如何处理的?

var
// Map over jQuery in case of overwrite
_jQuery = window.jQuery,//外部库重写了jQuery,先缓存 // Map over the $ in case of overwrite
_$ = window.$;//外部库重写了$,先缓存起来 jQuery.noConflict = function( deep ) {
if ( window.$ === jQuery ) {//把全局$的控制权交出去,此处,jQuery为局部变量
window.$ = _$;
} if ( deep && window.jQuery === jQuery ) {//把全局jQuery的控制权也交出去,此处局部jQuery与全局jQuery做对比
window.jQuery = _jQuery;
} return jQuery;
};

  如果我们需要同时使用jQuery和其他JavaScript库,我们可以使用 $.noConflict()把$的控制权交给其他库。旧引用的$ 被保存在jQuery的初始化; noConflict() 简单的恢复它们。
    通过类似swap交换的概念,先把之前的存在的命名空间给缓存起来_$,_jQuery,通过对比当前的命名空间达到交换的目的,首先,我们先判断下当前的的$空间是不是被jQuery接管了,如果是则让出控制权给之前的_$引用的库,如果传入deep为true的话等于是把jQuery的控制权也让出去了

那么具体何时调用才不会引起库命名空间冲突问题呢:这个函数必须在你导入jQuery文件之后,并且在导入另一个导致冲突的库之前使用。

jquery源码分析(二)——架构设计的更多相关文章

  1. jQuery 源码分析(二十一) DOM操作模块 删除元素 详解

    本节说一下DOM操作模块里的删除元素模块,该模块用于删除DOM里的某个节点,也可以理解为将该节点从DOM树中卸载掉,如果该节点有绑定事件,我们可以选择保留或删除这些事件,删除元素的接口有如下三个: e ...

  2. jQuery 源码分析(二十) DOM操作模块 插入元素 详解

    jQuery的DOM操作模块封装了DOM模型的insertBefore().appendChild().removeChild().cloneNode().replaceChild()等原生方法.分为 ...

  3. jQuery 源码分析(二) 入口模块

    jQuery返回的对象本质上是一个JavaScript对象,而入口模块则可以保存对应的节点的引用,然后供其它模块操作 我们创建jQuery对象时可以给jQuery传递各种不同的选择器,如下: fals ...

  4. Tomcat源码分析二:先看看Tomcat的整体架构

    Tomcat源码分析二:先看看Tomcat的整体架构 Tomcat架构图 我们先来看一张比较经典的Tomcat架构图: 从这张图中,我们可以看出Tomcat中含有Server.Service.Conn ...

  5. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

  6. [转]jQuery源码分析系列

    文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...

  7. jQuery源码分析系列(转载来源Aaron.)

    声明:非本文原创文章,转载来源原文链接Aaron. 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAa ...

  8. jQuery源码分析系列——来自Aaron

    jQuery源码分析系列——来自Aaron 转载地址:http://www.cnblogs.com/aaronjs/p/3279314.html 版本截止到2013.8.24 jQuery官方发布最新 ...

  9. [转] jQuery源码分析-如何做jQuery源码分析

    jQuery源码分析系列(持续更新) jQuery的源码有些晦涩难懂,本文分享一些我看源码的方法,每一个模块我基本按照这样的顺序去学习. 当我读到难度的书或者源码时,会和<如何阅读一本书> ...

随机推荐

  1. rnnlm源代码分析(八)

    系列前言 參考文献: RNNLM - Recurrent Neural Network  Language Modeling Toolkit(点此阅读) Recurrent neural networ ...

  2. Exchange 2013 的会议室邮箱用户一直无法正常登陆。

    某客户使用了Exchange 2013 server作为邮件承载server.详细版本号为Exchange 2013 SP1. 如今客户有个需求,希望他们的邮箱作为会议室邮箱创建,并且必须有普通邮箱全 ...

  3. codeforces Looksery Cup 2015 D. Haar Features

    The first algorithm for detecting a face on the image working in realtime was developed by Paul Viol ...

  4. linux驱动注册汇总

    --- 01)TP file_operations: { 1. static struct file_operations tpd_fops = { // .owner = THIS_MODULE, ...

  5. hdu1429胜利大逃亡(bfs)

    胜利大逃亡(续) Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Su ...

  6. ACM_魔仙岛探险(深搜)

    魔仙岛探险 Time Limit: 2000/1000ms (Java/Others) Problem Description: 小敏通过秘密方法得到一张不完整的魔仙岛航拍地图.魔仙岛由一个主岛和一些 ...

  7. HTML <!DOCTYPE>标签

    一般一个基本html页面的结构,如下代码所示: <html> <head> <title>我是基本的页面结构</title> </head> ...

  8. jar 包中文乱码注释显示问题解决方案

    通过maven下载源代码,直接通过eclipse浏览源代码时,发现中文注释为乱码的问题.其实这个eclipse默认编码造成的问题.可以通过以下方法解决: 修改Eclipse中文本文件的默认编码:win ...

  9. C#之纯数字判断

    public bool isNaN(string temp) { ; i <temp.Length; i++) { byte tempByte = Convert.ToByte(temp[i]) ...

  10. 有关css的选择器优先级以及父子选择器

    css,又称样式重叠表,如今的网页的样式基本是div+css写出来的,功能十分强大,要想在html文件中引入css文件需要在<head></head>标签内输入一行:<l ...