前言
Jquery(http://jquery.com/)是一个轻量级,快速简洁的Javascript框架,它的容量小巧,简洁和简短的语法,容易记;用户能更方便地处理HTML DOM、Events、实现动画效果,并且提供Ajax的支持。目前最新版本为 jQuery 1.3.1(http://jqueryjs.googlecode.com/files/jquery-1.3.1.js),这系列文章将对该版本的源码进行阐述。
现在开始本系列的第一篇,Jquery核心函数,内容主要包括:
分析
1. 在Jquery的应用开发中,我们经常看到这样的代码:
var width = $( " div .container " ).width(); // 得到div节点下样式值为container的宽度
var html = $(document.getElementById( " result " )).html(); // 得到id为result的节点的innerHTML值
$( " #result " , document.forms[ 0 ]).css( " color " , " red " ); // 将在第一个Form节点下id为result的字体颜色设置为红色
$( " <div>hello,world</div> " ).appendTo( " #result " ); // 将HTML字符串信息 内部追加到 id为result的节点末尾
那么$(...)里面的参数,Jquery API中是怎样辨认参数是表达式,id,HTML字符串,还是DOM元素呢?
现在我们来深入剖析Jquery源码。
2. 这里,我先来做个测试,我将Jquery API简化为这样的代码:
var window = this ,
jQuery = window.jQuery = window.$ = function (selector, context){
return new jQuery.fn.init(selector, context);
};
jQuery.fn = jQuery.prototype = {
init : function (selector, context) {
alert(selector); // 弹出警告框
}
};
})();
window.onload = function () {
$( " div .container " ); // 得到“div . container”
$( " #result " ); // 得到“#result”
$( " <div>hello,world</div> " ); // 得到“<div>hello,world</div>”
$(document.getElementById( " result " )); // 得到“[object]”
}
从这里我们可以得出,实际上$里面的参数(表达式字符串,ID字符串,HTML字符串,DOM对象),主要就是在init方法中各自实现它们自己的逻辑。
现在列出init方法的具体实现:
// Make sure that a selection was provided
selector = selector || document;
// Handle $(DOMElement)
if ( selector.nodeType ) {
this [ 0 ] = selector;
this .length = 1 ;
this .context = selector;
return this ;
}
// Handle HTML strings
if ( typeof selector === " string " ) {
// Are we dealing with HTML string or an ID?
var match = quickExpr.exec( selector );
// Verify a match, and that no context was specified for #id
if ( match && (match[ 1 ] || ! context) ) {
// HANDLE: $(html) -> $(array)
if ( match[ 1 ] )
selector = jQuery.clean( [ match[ 1 ] ], context );
// HANDLE: $("#id")
else {
var elem = document.getElementById( match[ 3 ] );
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem && elem.id != match[ 3 ] )
return jQuery().find( selector );
// Otherwise, we inject the element directly into the jQuery object
var ret = jQuery( elem || [] );
ret.context = document;
ret.selector = selector;
return ret;
}
// HANDLE: $(expr, [context])
// (which is just equivalent to: $(content).find(expr)
} else
return jQuery( context ).find( selector );
// HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) )
return jQuery( document ).ready( selector );
// Make sure that old selector state is passed along
if ( selector.selector && selector.context ) {
this .selector = selector.selector;
this .context = selector.context;
}
return this .setArray(jQuery.makeArray(selector));
}
3. 现在分析 表达式,id,HTML字符串,DOM元素等等各自的实现:
1)形如 $(document.getElementById("result")) 【jQuery(elements)】DOM元素的实现,通过init方法中的以下代码:
if ( selector.nodeType ) {
this [ 0 ] = selector;
this .length = 1 ;
this .context = selector;
return this ;
}
selector.nodeType判断当selector为元素节点时,将length置为1,并且赋值于context,实际上context作为init的第二个参数,它意味着它的上下文节点就是selector该点,返回它的$(...)对象。
2)形如 $("<div>hello,world</div>") 【jQuery(html,[ownerDocument])】HTML字符串的实现,通过init方法中的以下代码:
if ( typeof selector === " string " ) {
// quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/
// 利用检查正则表达式HTML字符串还是元素ID字符串
var match = quickExpr.exec( selector );
if ( match && (match[ 1 ] || ! context) ) {
// 处理HTML字符串
if ( match[ 1 ] )
selector = jQuery.clean( [ match[ 1 ] ], context );
// 处理形如$("#id")
else {
//
}
}
// 处理 形如 $("div .container")的表达式字符串
else
//
}
// 处理 形如 $(function) , $(document).ready(function(){})的表示
else if ( jQuery.isFunction( selector ) ) {
}
//
关键看到这样的一句代码,selector = jQuery.clean( [ match[1] ], context ); 继续查看clean都做了些什么:
这么长的一串代码 实际上最后访问的是一个ret为变量的数组,而数组中的元素变为以DOM元素的对象,而它的innerHTML正好就是刚才的HTML字符串。
3)形如 $("#result") 【jQuery(expression,[context])】ID字符串的实现,通过init方法中的以下代码:
else {
// match[3]得到ID的值如:result
var elem = document.getElementById( match[ 3 ] );
if ( elem && elem.id != match[ 3 ] )
return jQuery().find( selector );
// 调用jQuery(elements)方式
var ret = jQuery( elem || [] );
// 默认上下文DOM为window.document
ret.context = document;
ret.selector = selector;
return ret;
}
根据match[3]可以得到DOM对象elem,并且调用2)介绍的jQuery(elements),最后返回一个ret为变量的jquery对象。
4)形如 $("div .container") 【jQuery(expression,[context])】表达式字符串的实现,通过init方法中的以下代码:
else
return jQuery( context ).find( selector );
关键看到这样的一句代码,jQuery().find( selector ); 继续查看find都做了些什么:
// 当表达式不包含“,”符号时候
if ( this .length === 1 && ! / , / .test(selector) ) {
var ret = this .pushStack( [], " find " , selector );
ret.length = 0 ;
jQuery.find( selector, this [ 0 ], ret );
return ret;
}
// 当表达式包含“,”符号时候
else {
var elems = jQuery.map( this , function (elem){
return jQuery.find( selector, elem );
});
return this .pushStack( / [^+>] [^+>] / .test( selector ) ?
jQuery.unique( elems ) :
elems, " find " , selector );
}
}
先看下表达式不包含“,”符号的时候,调用pushStack方法,方法为:
pushStack: function ( elems, name, selector ) {
var ret = jQuery( elems );
// 将上个对象的引用推入栈中
ret.prevObject = this ;
ret.context = this .context;
// 关键字为find时,在原有selector的基础上,继续增加selector
// 如 $("div").find("p") 意思就是 $("div p")
if ( name === " find " )
ret.selector = this .selector + ( this .selector ? " " : "" ) + selector;
else if ( name )
ret.selector = this .selector + " . " + name + " ( " + selector + " ) " ;
// 返回最新的Jquery对象
return ret;
}
注意这里看到 ret.prevObject = this; 这个方法在$(...).andSelf()和$(...).end()中调用,对于筛选或查找后的元素,返回前一次元素状态它是很有用的。
接着调用 jQuery.find( selector, this[0], ret ); ,首先我们看到有这样的几句代码:
jQuery.filter = Sizzle.filter;
jQuery.expr = Sizzle.selectors;
jQuery.expr[ " : " ] = jQuery.expr.filters;
//
window.Sizzle = Sizzle;
jQuery.find方法转去调用全局的Sizzle对象了(实际上这里运用到了Javascript设计模式中的适配器模式,jquery.find实际上调用的是Sizzle的对象。关于Javascript适配器模式,我将接下来的Javascript乱弹设计模式系列文章中具体叙述),Sizzle对象定义为:
呵呵,好长的一段代码,实际最关键的一句代码:Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
对表达式字符串进行解析,最后返回一个jQuery对象,它就是 表达式字符串 最后想要的jQuery对象。
当表达式包含“,”符号的时候,查看这样的一句代码:return this.pushStack( /[^+>] [^+>]/.test( selector ) ? jQuery.unique( elems ) : elems, "find", selector );
最后也是返回一个jQuery对象,它就是 表达式字符串 最后想要的jQuery对象。
5)最后一点,形如 $(function) , $(document).ready(function(){})的表示,通过init方法中的以下代码来实现:
else if ( jQuery.isFunction( selector ) )
return jQuery( document ).ready( selector );
如果判断selector是函数的话,将执行jQuery(document).ready(selector);
ready方法具体为:
// 绑定事件监听
bindReady();
if ( jQuery.isReady )
fn.call( document, jQuery );
else
jQuery.readyList.push( fn );
return this ;
}
bindReady方法将事件绑定在文档加载完毕之后,最后通过调用fn.call( document, jQuery );来激发事件的执行。
好了,jQuery的核心函数的原理机制就是这样的,下一篇我将谈下jQuery对象访问和数据缓存的原理机制。