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的应用开发中,我们经常看到这样的代码:
$(
"
div .container
"
).css(
"
display
"
,
"
none
"
);
//
将div节点下样式值为container的节点设置为不显示
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简化为这样的代码:
(
function
(){
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方法的具体实现:
init:
function
( selector, context ) {
//
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方法中的以下代码:
//
Handle $(DOMElement)
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方法中的以下代码:
//
判断selector为字符串
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都做了些什么:
Code
clean: function( elems, context, fragment ) {
context = context || document;
// !context.createElement fails in IE with an error but returns typeof 'object'
if ( typeof context.createElement === "undefined" )
context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
// If a single string is passed in and it's a single tag
// just do a createElement and skip the rest
if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) {
var match = /^<(/w+)/s*//?>$/.exec(elems[0]);
if ( match )
return [ context.createElement( match[1] ) ];
}
var ret = [], scripts = [], div = context.createElement("div");
jQuery.each(elems, function(i, elem){
if ( typeof elem === "number" )
elem += '';
if ( !elem )
return;
// Convert html string into DOM nodes
if ( typeof elem === "string" ) {
// Fix "XHTML"-style tags in all browsers
elem = elem.replace(/(<(/w+)[^>]*?)//>/g, function(all, front, tag){
return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
all :
front + "></" + tag + ">";
});
// Trim whitespace, otherwise indexOf won't work as expected
var tags = jQuery.trim( elem ).toLowerCase();
var wrap =
// option or optgroup
!tags.indexOf("<opt") &&
[ 1, "<select multiple='multiple'>", "</select>" ] ||
!tags.indexOf("<leg") &&
[ 1, "<fieldset>", "</fieldset>" ] ||
tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
[ 1, "<table>", "</table>" ] ||
!tags.indexOf("<tr") &&
[ 2, "<table><tbody>", "</tbody></table>" ] ||
// <thead> matched above
(!tags.indexOf("<td") || !tags.indexOf("<th")) &&
[ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
!tags.indexOf("<col") &&
[ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
// IE can't serialize <link> and <script> tags normally
!jQuery.support.htmlSerialize &&
[ 1, "div<div>", "</div>" ] ||
[ 0, "", "" ];
// Go to html and back, then peel off extra wrappers
div.innerHTML = wrap[1] + elem + wrap[2];
// Move to the right depth
while ( wrap[0]-- )
div = div.lastChild;
// Remove IE's autoinserted <tbody> from table fragments
if ( !jQuery.support.tbody ) {
// String was a <table>, *may* have spurious <tbody>
var tbody = !tags.indexOf("<table") && tags.indexOf("<tbody") < 0 ?
div.firstChild && div.firstChild.childNodes :
// String was a bare <thead> or <tfoot>
wrap[1] == "<table>" && tags.indexOf("<tbody") < 0 ?
div.childNodes :
[];
for ( var j = tbody.length - 1; j >= 0 ; --j )
if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
tbody[ j ].parentNode.removeChild( tbody[ j ] );
}
// IE completely kills leading whitespace when innerHTML is used
if ( !jQuery.support.leadingWhitespace && /^/s/.test( elem ) )
div.insertBefore( context.createTextNode( elem.match(/^/s*/)[0] ), div.firstChild );
elem = jQuery.makeArray( div.childNodes );
}
if ( elem.nodeType )
ret.push( elem );
else
ret = jQuery.merge( ret, elem );
});
if ( fragment ) {
for ( var i = 0; ret[i]; i++ ) {
if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
} else {
if ( ret[i].nodeType === 1 )
ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
fragment.appendChild( ret[i] );
}
}
return scripts;
}
return ret;
}
这么长的一串代码 实际上最后访问的是一个ret为变量的数组,而数组中的元素变为以DOM元素的对象,而它的innerHTML正好就是刚才的HTML字符串。
3)形如 $("#result") 【jQuery(expression,[context])】ID字符串的实现,通过init方法中的以下代码:
//
处理形如$("#result")
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方法中的以下代码:
//
处理 形如 $("div .container")的表达式字符串
else
return
jQuery( context ).find( selector );
关键看到这样的一句代码,jQuery().find( selector ); 继续查看find都做了些什么:
find:
function
( selector ) {
//
当表达式不包含“,”符号时候
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.find
=
Sizzle;
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对象定义为:
Code
var Sizzle = function(selector, context, results, seed) {
results = results || [];
context = context || document;
if ( context.nodeType !== 1 && context.nodeType !== 9 )
return [];
if ( !selector || typeof selector !== "string" ) {
return results;
}
var parts = [], m, set, checkSet, check, mode, extra, prune = true;
// Reset the position of the chunker regexp (start from head)
chunker.lastIndex = 0;
while ( (m = chunker.exec(selector)) !== null ) {
parts.push( m[1] );
if ( m[2] ) {
extra = RegExp.rightContext;
break;
}
}
if ( parts.length > 1 && origPOS.exec( selector ) ) {
if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
set = posProcess( parts[0] + parts[1], context );
} else {
set = Expr.relative[ parts[0] ] ?
[ context ] :
Sizzle( parts.shift(), context );
while ( parts.length ) {
selector = parts.shift();
if ( Expr.relative[ selector ] )
selector += parts.shift();
set = posProcess( selector, set );
}
}
} else {
var ret = seed ?
{ expr: parts.pop(), set: makeArray(seed) } :
Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
set = Sizzle.filter( ret.expr, ret.set );
if ( parts.length > 0 ) {
checkSet = makeArray(set);
} else {
prune = false;
}
while ( parts.length ) {
var cur = parts.pop(), pop = cur;
if ( !Expr.relative[ cur ] ) {
cur = "";
} else {
pop = parts.pop();
}
if ( pop == null ) {
pop = context;
}
Expr.relative[ cur ]( checkSet, pop, isXML(context) );
}
}
if ( !checkSet ) {
checkSet = set;
}
if ( !checkSet ) {
throw "Syntax error, unrecognized expression: " + (cur || selector);
}
if ( toString.call(checkSet) === "[object Array]" ) {
if ( !prune ) {
results.push.apply( results, checkSet );
} else if ( context.nodeType === 1 ) {
for ( var i = 0; checkSet[i] != null; i++ ) {
if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
results.push( set[i] );
}
}
} else {
for ( var i = 0; checkSet[i] != null; i++ ) {
if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
results.push( set[i] );
}
}
}
} else {
makeArray( checkSet, results );
}
if ( extra ) {
Sizzle( extra, context, results, seed );
}
return results;
};
呵呵,好长的一段代码,实际最关键的一句代码: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 );
它的意思是当表达式字符串包含“+”,“>”作为选择器(比如$("form > input") 或者 $("label + input") )的时候,将作为jQuery.map返回值的elems数组删除重复的元素,这个是有必要的。
最后也是返回一个jQuery对象,它就是 表达式字符串 最后想要的jQuery对象。
5)最后一点,形如 $(function) , $(document).ready(function(){})的表示,通过init方法中的以下代码来实现:
//
处理 形如 $(function) , $(document).ready(function(){})的表示
else
if
( jQuery.isFunction( selector ) )
return
jQuery( document ).ready( selector );
如果判断selector是函数的话,将执行jQuery(document).ready(selector);
ready方法具体为:
ready:
function
(fn) {
//
绑定事件监听
bindReady();
if
( jQuery.isReady )
fn.call( document, jQuery );
else
jQuery.readyList.push( fn );
return
this
;
}
bindReady方法将事件绑定在文档加载完毕之后,最后通过调用fn.call( document, jQuery );来激发事件的执行。
好了,jQuery的核心函数的原理机制就是这样的,下一篇我将谈下jQuery对象访问和数据缓存的原理机制。
谢谢大家阅读
!
转自:http://www.cnblogs.com/liping13599168/archive/2009/02/09/1386623.html