jQuery Sizzle 入口 [ 源碼分析 ]
來源:程序員人生 發布時間:2014-09-15 01:04:03 閱讀次數:2663次
var Sizzle = function( selector, context, results, seed ) {
//context 默認為document,可以人為指定
results = results || [];
context = context || document;
var origContext = context;
//判斷文檔節點
if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
return [];
}
//表達式是否為字符串
if ( !selector || typeof selector !== "string" ) {
return results;
}
//set: 種子集
//checkSet: 種子集的副本
//extra: 保存剩余的并聯表達式,注意是并聯表達式
//ret: 初步返回的結果,數據類型為json,包含set與expr屬性,set為種子集,expr為剩余的塊表達式
//cur: 塊表達式關系符如:+ > ~ ,如果沒有則默認為空格 " "
//pop:彈出數組的最后一個變量
//parts: 存儲當前被分割的塊表達式數組
var m, set, checkSet, extra, ret, cur, pop, i,
prune = true,
contextXML = Sizzle.isXML( context ),
parts = [],
soFar = selector;
// Reset the position of the chunker regexp (start from head)
// 分割快表達式,對于碰到并聯表達式,則暫時結束,將余下的并聯表達式保存在extra中
// 如#info .p,div.red > a
do {
chunker.exec( "" );
m = chunker.exec( soFar );
if ( m ) {
soFar = m[3];
parts.push( m[1] );
//根據上面的chunker正則,如果存在并聯表達式,那么exec之后,m[2]的值為 ","
if ( m[2] ) {
//m[3]為剩余的并聯表達式
extra = m[3];
break;
}
}
} while ( m );
//會被切割為如下:
//parts ["#info",".p"] ,extra : div.red > a
//判斷是否存在位置偽類 origPOS,如 :frist,:last 等,如果存在位置偽類,則采取自左向右搜索
if ( parts.length > 1 && origPOS.exec( selector ) ) {
if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
set = posProcess( parts[0] + parts[1], context, seed );
} 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, seed );
}
}
} else {
// Take a shortcut and set the context if the root selector is an ID
// (but not if it'll be faster if the inner selector is an ID)
// 這里主要修改context,如果表達式的開頭為ID類型,且最后一個表達式非ID類型
// 所有的查詢,使用到sizzle.selector.find的,只有最后一個節點,其它的節點都可以采用關系來決定
// 之所以只有第一個為id的時候可以修改context,且最后一個不能為ID,因為getElementById只在document中存在
// 在element中不存在該方法,如最后一個元素為ID,那么直接就會報錯
// 那么將context修改為ID所在節點,用來提高效率
// 如:$("#info .p"); 那么自動修改為如下: $(".p",$("#info"));
if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
//調用find方法,查詢parts中的第一個元素,返回臨時結果
//如以上的#info .p 那么會直接查詢#info ,返回結果
ret = Sizzle.find( parts.shift(), context, contextXML );
//然后修改context
context = ret.expr ?
Sizzle.filter( ret.expr, ret.set )[0] :
ret.set[0];
}
if ( context ) {
//因為是采用自右向左的方式搜索,那么先獲取出數組的最后一個元素,調用parts.pop();
//再調用find方法查詢
ret = seed ?
{ expr: parts.pop(), set: makeArray(seed) } :
Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
//對結果進行過濾
set = ret.expr ?
Sizzle.filter( ret.expr, ret.set ) :
ret.set;
if ( parts.length > 0 ) {
checkSet = makeArray( set );
} else {
prune = false;
}
//繼續操作塊表達式中最后一個元素左邊的部分,如果存在,調用關系正則判斷
//如 1 .div.red > p,那么上面已經查詢過p了,那么現在parts中的元素為['div.red','>'];
// 2 .div.red p,那么上面已經查詢過p了,那么現在parts中的元素為['div.red'];
while ( parts.length ) {
//取出最后一個元素,即為'>' ,如果為第二種情況,那么直接就是 'div.red'
cur = parts.pop();
pop = cur;
//是否存在關系符號,如果為空格
if ( !Expr.relative[ cur ] ) {
//查詢是否存在cur類型,如為第二種情況,那么給cur賦值為"",這時的pop 則為div.red
cur = "";
} else {
//取出上一級元素,如為第一種情況,需要重新取最后一個元素,為div.red
pop = parts.pop();
}
//如果pop為空,那么默認為document
if ( pop == null ) {
pop = context;
}
//調用關系正則判斷,pop為左邊元素的表達式,checkSet為待過濾元素,cur 為關系符
//這里要強調的一點是,可以有人會發現,這里是直接調用,沒有返回值,
//那么過濾之后的得到的結果怎么返回呢?
//這里是采用了javascript函數對數組的傳遞是傳值的方式,調用的函數中修改checkSet的值
//那么在后面去使用checkSet的值,也會變化
Expr.relative[ cur ]( checkSet, pop, contextXML );
}
} else {
checkSet = parts = [];
}
}
if ( !checkSet ) {
checkSet = set;
}
if ( !checkSet ) {
Sizzle.error( cur || selector );
}
if ( toString.call(checkSet) === "[object Array]" ) {
if ( !prune ) {
results.push.apply( results, checkSet );
} else if ( context && context.nodeType === 1 ) {
for ( i = 0; checkSet[i] != null; i++ ) {
if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
results.push( set[i] );
}
}
} else {
for ( 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, origContext, results, seed );
//對結果排序去重,合并
Sizzle.uniqueSort( results );
}
return results;
};
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈