jQuery是目前最常用的一款开源JS框架。
最新的realease版本是1.7.1,其体积已经达到了229k了。这么多代码肯定不是一蹴而就的,为了更好的学习jQuery的源码、更好的体会一个优秀的开源JS框架的诞生和成长历史。我们从jQery的最初版本开始分析体会,看看jQuery的成长历代记。
很傻X的看到第一行的代码:
window.undefined = window.undefined;
为啥要这么写呢?这样赋值应该和未赋值没有任何区别才对,不过我相信这样一个框架不会做这种很明显多余的动作。(况且我在Ext的源码中也看到过这一句)
这一句是什么意思呢?
原来早期版本的某些浏览器没有全局变量undefined,所以,右边的 window.undefined就会返回undefined,然后把它赋值给window.undefined。
下面就是jQuery的构造器部分的定义:
function jQuery( a , c ) {
// Shortcut for document ready (because $(document).each() is silly)
if ( a && a.constructor == Function && jQuery.fn.ready )
return jQuery( document ).ready( a );
//******************这里省略了很多代码***************************************
}
先看第1句 if ( a && a.constructor == Function && jQuery.fn.ready ) return jQuery( document ).ready( a );
从这句话来看jQuery还是一个普通的function需要使用括号进行执行,我们知道jQuery的第1个参数是选择器参数,可以传递function、字符串等。这一句的意思是首先判断传递进来的选择器是否是一个function,并且后续代码中jQuery.fn.ready已经成功加载。若全满足则jQuery(function(){});等同于jQuery(document).ready(function(){});
这也是那个大家使用jQuery时最最常用的jQuery(function(){})的由来,它是一个简写。
我们再看看到了jQuery 1.1时代时这部分的实现方法:
if ( jQuery.isFunction( a ) && !a.nodeType && a[0] == undefined )
return new jQuery( document )[jQuery.fn.ready ? "ready" : "load"]( a );
这里的不同是从jQuery 1.1时代开始把一些常用的诸如判断是否为function之类的方法抽象出来了,也越发严谨了一些(判断a不是数组并且a也不是一个Element类型的对象)。需要注意的是这句话中多了一个load哦~至于load和ready的却别后面会详细阐述。
我们再看看jQuery 1.2时代时这部分:
if ( jQuery.isFunction( selector ) )
return jQuery( document )[ jQuery.fn.ready ? "ready" : "load" ]( selector );
因为到了1.2开始会先验证DOM等后判断是否为funciton。
... .....
最后到了1.4+时代:
if ( jQuery.isFunction( selector ) )
return rootjQuery.ready( selector );
至于rootjQuery是个什么东东??
// All jQuery objects should point back to these
rootjQuery = jQuery(document);
这下知道了,所谓的rootjQuery就是jQuery(document)的引用。为啥要这样用呢?
因为jQuery从1.5开始引入了jQuery._Deferred,有点类似于缓冲Buffer,后面会进行详解。
所以学习jQuery的源码还是从早期版本看起比较好这样可以比较容易的分析出来设计思路。后期封装的太厉害了,很难梳理出头绪。
//***************我是一个分界*************************
好吧。让我们回到jQuery年轻的1.01时代:
return jQuery( document ).ready( a );
这里面的ready在后面进行说明,暂时先往下看:
// Make sure that a selection was provided
a = a || jQuery.context || document;
这个地方就体现出来1.0版本的稚嫩性了,应该先判断参数后进行处理。这里先判断是否为function而后又处理参数a显得逻辑混乱,从jQuery1.2开始这部分就被修改了。
// Watch for when a jQuery object is passed as the selector
if ( a.jquery ) return $( jQuery.merge( a , [] ) );
如果传递进来的是一个jQuery类型的对象则执行merge:
merge : function( first , second ) {
var result = [];
// Move b over to the new array (this helps to avoid StaticNodeList instances)
for ( var k = 0; k < first.length; k++ )
result[k] = first[k];
// Now check for duplicates between a and b and only add the unique items
for ( var i = 0; i < second.length; i++ ) {
var noCollision = true;
// The collision-checking process
for ( var j = 0; j < first.length; j++ ){
if ( second[i] == first[j] ) noCollision = false;
}
// If the item is unique, add it
if ( noCollision ) result.push( second[i] );
}
return result;
}
这段代码非常简单就是创建一个数组将first数组的全部内容以及second数组中和first数组不一样的内容抽取出来进行合并,并返回这个新数组的方法。
// Watch for when a jQuery object is passed at the context
if ( c && c.jquery ) return $( c ).find( a );
// If the context is global, return a new object
if ( window == this ) return new jQuery( a , c );
代码的上半部分说明如果调用jQuery时包含上下文部分则直接执行上下文中过滤a的代码。
下半部分放在这个地方有些不合适,因为是上下文判断所以应该放在最上面才对。
如果调用是window.jQuery(a,c)就相当于直接返回一个jQuery的实例new jQuery(a,c);
后面是验证字符串类型的参数
// Handle HTML strings
var m = /^[^<]*(<.+>)[^>]*$/.exec( a );
if ( m ) a = jQuery.clean( [ m[1] ] );
首先将字符串a进行处理提取html tag部分内容,但是这里并没有对a的类型进行判断是个问题。
从jQuery 1.1开始增加了对类型的判断
// Handle HTML strings
if ( typeof a == "string" ) {
var m = /^[^<]*(<.+>)[^>]*$/.exec( a );
a = m ?
// HANDLE: $(html) -> $(array)
jQuery.clean( [ m[1] ] ) :
// HANDLE: $(expr)
jQuery.find( a , c );
}
下面是对数组类型的判断
// Watch for when an array is passed in
this.get( a.constructor == Array || a.length && !a.nodeType
&& a[0] != undefined && a[0].nodeType ?
// Assume that it is an array of DOM Elements
jQuery.merge( a , [] ) :
// Find the matching elements and save them for later
jQuery.find( a , c ) );
因为一个对象实例的constructor(构造器)永远指向他的类(class)本身。所以如果a的对象类型是数组则a.constructor==Array。
请注意一点的是逻辑与(&&)的优先级要大于逻辑或(||),所以上面的3元运算符判断部分添加括号后应该是:
a.constructor == Array || (a.length && !a.nodeType && (a[0] != undefined) && a[0].nodeType)
后面的注释就解释的很清楚喽。
// See if an extra function was provided
var fn = arguments[arguments.length - 1];
// If so, execute it in context
if ( fn && fn.constructor == Function ) this.each( fn );
根据这2句判断当时还可以传递第3个参数,类型为function。若传递此参数就执行jQuery.each。
为啥用this呢?if ( window == this ) return new jQuery( a , c );
jQuery的构造器分析完看下面的代码:
// Map over the $ in case of overwrite
if ( typeof $ != "undefined" ) jQuery._$ = $;
// Map the jQuery namespace to the '$' one
var $ = jQuery;
上面一句判断是否当前$已经被使用了,比如同时使用了Prototype框架和jQuery框架,且先加载了Prototype。则将Prototype的$指定到jQuery._$中。
将jQuery指定为$ 。这里说明当时jQuery还是很霸道的,总是会覆盖其他框架的$方法。这里说覆盖也不对应该是优先占用$,使用户代码优先被jQuery执行。
下面介绍jQuery的原型。
为啥要介绍原型呢?我们通常使用jQuery的使用都是var a = $("<p>a</p>");或者$(function(){/* do something */});
$( /* something */)相当于window.$(/* something */);
还记得上面介绍的if ( window == this ) return new jQuery( a , c );
所以每一个生成的jQuery对象都包含原型中定义的方法。下面上代码:
jQuery.fn = jQuery.prototype = {
jquery : "$Rev: 509 $",
size : function(){... ...},
get : function( num ){... ...},
each : function( fn , args ){... ...},
index : function( obj ){... ...},
attr : function ( key , value , type ) {... ...},
css : function ( key , value ){... ...},
text : function ( e ) {... ...},
wrap : function() {... ...},
append : function() {... ...},
prepend : function() {... ...},
before : function() {... ...},
after : function() {... ...},
end : function() {... ...},
find : function( t ) {... ...},
clone : function( deep ) {... ...},
filter : function( t ) {... ...},
not : function( t ) {... ...},
add : function( t ) {... ...},
is : function( expr ) {... ...},
domManip : function( args , table , dir , fn ) {... ...},
pushStack : function( a , args ) {... ...}
}
第1个方法是size方法定义如下:
size : function() {return this.length;}
就1句而已,返回当前对象的长度。至于当前对象为啥有length这个属性?是因为整个jQuery结果集就是一个数组还是其他的原因稍后会说明。
第2个方法是get ,这个方法的作用和代码的含义需要结合该方法的使用来说明,我们看下jQuery的API:
get()、get(index)
取得所有匹配的 DOM 元素集合。
这是取得所有匹配元素的一种向后兼容的方式(不同于jQuery对象,而实际上是元素数组)。
如果你想要直接操作 DOM 对象而不是 jQuery 对象,这个函数非常有用。
请看实例:
HTML代码如下:
<ul>
<li id="foo">foo</li>
<li id="bar">bar</li>
</ul>
若执行无参数调用 alert($('li').get());
则返回: [<li id="foo">, <li id="bar">]
若传递index参数则:($('li').get(0));
返回:<li id="foo">
我们需要注意API原文的这里:‘
Each jQuery object also masquerades as an array, so we can use the array dereferencing operator to get at the list item instead
jQuery对象均被伪装成数组的样子所以你可以通过数组下标方式获取元素,上面的写法等价于:alert($('li')[0]);
需要注意的是如果index传递的值为负数则结果为从数组最后一个结果向前查询例如:alert($('li').get(-1))
将返回:<li id="bar">
源码定义如下:
get : function( num ) {
// Watch for when an array (of elements) is passed in
if ( num && num.constructor == Array ) {
// Use a tricky hack to make the jQuery object
// look and feel like an array
this.length = 0;
[ ].push.apply( this , num );
return this;
} else{
return num == undefined ?
// Return a 'clean' array
jQuery.map( this , function( a ) { return a } ) :
// Return just the object
this[num];
}
}
需要注意的是这里的this,根据前面API的描述这个this已经是被伪装成的数组了,所以如果传递的参数num是一个数组(API中描述这个参数只能是一个索引值,所以这一段代码应该根本不会被执行只能作为容错)。则将参数传递的数组添加(push)到this代表的数组中。
这里可以学习一个小技巧: [ ].push.apply( this , num ); 另一种写法Array.prototype.push.apply(this,num);
Ext用的后一种写法,我感觉后一种更好一些。
到了jQuery 1.3.2后看看get方法的实现:
get: function( num ) {
return num === undefined ?
// Return a 'clean' array
Array.prototype.slice.call( this ) :
// Return just the object
this[ num ];
}
这里用的就是我说的方式了。注意这里已经不再判断num是否为一个数组了。(我就说那个判断没用嘛~~~~)
PS : Array 的 push() 方法作用:向数组的末尾添加一个或更多元素,并返回新的长度。
Array 的 slice()方法的作用:从某个已有的数组返回选定的元素。这个方法可以传递2个参数,如果不传参数,就是返回数组所有元素也就是返回整个数组。
PS2:slice带参数的写法是 Array.prototype.slice.call( this ,0 ) 带1个参数的写法和不写0效果相同。带2个参数就是 Array.prototype.slice.call( this , 0 , 1 )含义就不说了。不知道的去问谷哥哥。
我们回头看jQuery 1.01 get方法的最后1句:
return num == undefined ? jQuery.map( this , function( a ) { return a } ) : this[num];
含义就是判断num参数是否存在存在则返回特定元素即this[num] ,还记得API里说的嘛?”jQuery对象均被伪装成数组的样子所以你可以通过数组下标方式获取元素,上面的写法等价于:alert($('li')[0]);“这回就知道为什么会这样了,因为内部jQuery自己就是这么实现的。
那个返回全部的方法在jQuery 1.3.2时已经变成了 Array.prototype.slice.call( this ) 这种写法肯定比jQuery.map( this , function( a ) { return a } )要高,这里也可以看出随着jQuery的升级对代码的重构会有意识的把封装类的方法替换为js原生方法以提高效率。
虽然我们已经知道Array.prototype.slice.call( this )的含义了,但是jQuery.map( this , function( a ) { return a } )是如何处理的呢?我们看一下map方法的代码:
map : function( elems , fn ) {
// ********************这里省略了一些代码**********************
for ( var i = 0; i < elems.length; i++ ) {
var val = fn( elems[i] , i );
if ( val !== null && val != undefined ) {
if ( val.constructor != Array ) {
val = [ val ];
}
result = jQuery.merge( result , val );
}
}
return result;
}
重点看一下参数为数组时调用的merge,还记得merge嘛?前面提到的,使用的是循环拷贝数组对象的方式。这种方式相当于自己实现了一遍 Array.prototype.slice.call( this )的功能,所以效率必然要低啊。。
这些工具类的merge、map等方法,我们在后面进行专门阐述哦。
分享到:
相关推荐
**jQuery公司大事记纵向时间轴详解** jQuery是一个广泛使用的JavaScript库,它极大地简化了JavaScript的DOM操作、事件处理、动画制作以及Ajax交互。这个“jQuery公司大事记纵向时间轴”很可能是展示jQuery自诞生...
jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码...
【jQuery版公司大事记时间轴】是一个用于展示企业或组织发展历程的时间线特效代码,它采用JavaScript库jQuery实现,能够以视觉吸引人的方式呈现一系列重要的事件和里程碑。在网页设计中,这种时间轴特效能帮助访客更...
Ajax大事记,PHP+jQuery数据库版,程序根据系统生成的时间自动将同一年的事件归类,并可在其中插入图片,Ajax显示图片;运行滚动条特效拖动显示每一年份的重大事件,适用于PHP环境的网站。 演示地址:...
**jQuery公司大事记时间轴详解** jQuery是一款广泛应用于Web开发的JavaScript库,它极大地简化了JavaScript的DOM操作、事件处理、动画设计以及Ajax交互。在Web开发领域,jQuery以其高效、易用和丰富的插件生态而...
jquery-3.7.0.min.js(jQuery下载)jquery-3.7.0.min.js(jQuery下载)jquery-3.7.0.min.js(jQuery下载)jquery-3.7.0.min.js(jQuery下载)jquery-3.7.0.min.js(jQuery下载)jquery-3.7.0.min.js(jQuery下载)...
【jQuery仿百度万年历代码】是一个利用JavaScript库jQuery实现的日期选择插件,它旨在模仿百度搜索引擎中的万年历功能,提供用户友好的日期选取体验。这个插件特别强调了对农历日期的支持,使得在网页应用中可以方便...
1 jQuery简介 jQuery优势 jQuery安装 jQuery语法 1、jQuery简介 1.1 学习jQuery之前,需要以下基础知识 HTML CSS JavaScript 1、jQuery简介 1.2 什么是jQuery? jQuery是一个JavaScript函数库 轻量级,“写的少,做的...
《jQuery水平滑动企业大事记时间轴代码详解》 在当今网页设计中,动态效果的运用使得用户体验得到了显著提升,其中,时间轴布局是展示企业发展历程或重要事件的常用方式。"jQuery水平滑动企业大事记时间轴代码"提供...
jquery插件库(jquery.treeview插件库)jquery插件库(jquery.treeview插件库)jquery插件库(jquery.treeview插件库)jquery插件库(jquery.treeview插件库)jquery插件库(jquery.treeview插件库)jquery插件库(jquery....
jquery-3.3.1.js和jquery-3.3.1.min.js免费下载哈。jquery-3.3.1.js和jquery-3.3.1.min.js免费下载哈。jquery-3.3.1.js和jquery-3.3.1.min.js免费下载哈。jquery-3.3.1.js和jquery-3.3.1.min.js免费下载哈。jquery-...
资源名称:jQuery、jQuery UI及jQuery Mobile技巧与示例内容简介:《jQuery、jQuery UI及jQuery Mobile技巧与示例》包括jQuery、jQuery UI、jQuery Mobile以及jQuery插件四部分内容。第一部分介绍jQuery核心库,从...
`jQuery1.11.0_20140330.chm`可能是早期版本的jQuery API参考,而`jQueryAPI_CHM.CHM`则是针对jQuery 1.x系列的API手册,可能包含了1.12.4版本的相关信息。 在这些API文档中,你可以找到jQuery的函数、方法、选择器...
jquery 精简版 jquery 精简版 jquery 精简版jquery 精简版 jquery 精简版 jquery 精简版 jquery 精简版
jquery插件库大全(200个): jqueryQQ表情插件 jquery下拉菜单导航 jquery下拉菜单栏 jquery仿Windows系统选中图标效果 jquery仿京东商品详情页图片放大效果 jquery仿百度新闻焦点轮播 jquery分离布局模版 jquery...
jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,jquery,...
PHP jQuery大事记,程序根据系统生成的时间自动将同一年的事件归类,并可在其中插入图片,Ajax显示图片;运行滚动条特效拖动显示每一年份的重大事件,适用于PHP环境的网站。调试:因只是一个模块,所以没有写安装...
**jQuery 是一个高效、简洁且功能丰富的 JavaScript 库,它极大地简化了JavaScript代码的编写,使得网页交互变得更加简单。在“jQuery例子大全 jQuery demo”这个压缩包中,包含了一系列的示例,旨在帮助用户快速...
编写基于Jquery的表单验证插件 ...1、初步运用了Jquery框架进行编程,基本掌握Jquery框架的使用。 2、熟练使用Jquery-validate表单验证插件,并熟知实现原理。 3、基本实现了一个基于Jquery的表单验证的调查问卷。