博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
jquery 源码分析七 - Sizzle
阅读量:4922 次
发布时间:2019-06-11

本文共 7769 字,大约阅读时间需要 25 分钟。

     这篇主要是分析Sizzle引擎中最关键的一个函数Sizzle,这个函数接受四个参数selector,context,results,seed,这四个参数的意义分别是选择器,上下文环境,保存结果数组,还有一个未知。。

     由于这个函数里面涉及了许多相关函数,所以先对主函数进行分析,并只是列出相关函数的作用。相关函数的分析会放在文章末尾。接下来先看主函数:

1 function Sizzle( selector, context, results, seed ) {  2     var match, elem, m, nodeType,  3         i, groups, old, nid, newContext, newSelector;  4         //各种常量定义  5     //设置合理的root节点,在网页中为document  6     if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {  7         setDocument( context );  8     }  9     //确定上下文环境和结果数组 10     context = context || document; 11     results = results || []; 12     //如果无selector或者selector不为字符串,则直接返回results 13     if ( !selector || typeof selector !== "string" ) { 14         return results; 15     } 16     //如果上下文环境的节点不是element或者document的话,就直接返回 空数组 17     if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { 18         return []; 19     } 20  21     //documentIsHTML 用于确定工作环境是否是html 22     if ( documentIsHTML && !seed ) { 23  24         // rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/ 25         // 用于快速的检测是否是单一的ID,CLASS或TAG选择符 26         if ( (match = rquickExpr.exec( selector )) ) { 27             // ID选择 28             if ( (m = match[1]) ) { 29                 if ( nodeType === 9 ) { 30                     elem = context.getElementById( m ); 31                     // 检测是否有parentNode存在,有些浏览器会返回已经被删除了的节点 32                     if ( elem && elem.parentNode ) { 33                         // 处理在IE, Opera, 和Webkit 会返回name等于 m 的元素 34                         if ( elem.id === m ) { 35                             results.push( elem ); 36                             return results; 37                         } 38                     } else { 39                         return results; 40                     } 41                 } else { 42                     // 如果context不是document的话,要检测context对elem的包含情况。 43                     if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && 44                         contains( context, elem ) && elem.id === m ) { 45                         results.push( elem ); 46                         return results; 47                     } 48                 } 49  50             // TAG选择 51             } else if ( match[2] ) { 52                 push.apply( results, context.getElementsByTagName( selector ) ); 53                 return results; 54  55             // CLASS选择 56             } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { 57                 push.apply( results, context.getElementsByClassName( m ) ); 58                 return results; 59             } 60         } 61  62         // querySelectorAll,处理复杂的选择 63         // rbuggyQSA用于检测querySelectAll的怪异特性 64         if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { 65             //expando是sizzle中唯一特征码 66             nid = old = expando; 67             newContext = context; 68             newSelector = nodeType === 9 && selector; 69  70              71             // querySelectorAll在element为根元素时行为很奇怪 72             // 我们可以给根元素一个特定的ID值 73             // 这样在selector最前面加上ID,就可以做到很好的筛选 74             // IE8中不支持在对象元素上用 75             if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { 76                 //tokenize的作用是将selector中的筛选符分割开来并放到相应数组中 77                 //例:tokenize('div#a') 78                 // --->[[ 79                 //      {matches:{0:div,index:0,input:'div#a'}, 80                 //       type:'TAG', 81                 //       value:'div'}, 82                 //       {matches:{0:a,index:0,input:'div#a'}, 83                 //       type:'ID', 84                 //       value:'#a'} 85                 //      ]] 86                 groups = tokenize( selector ); 87  88                 //如果context存在id,那么就是用原来的id,否则就用expando 89                 if ( (old = context.getAttribute("id")) ) { 90                     nid = old.replace( rescape, "\\$&" ); 91                 } else { 92                     context.setAttribute( "id", nid ); 93                 } 94                 nid = "[id='" + nid + "'] "; 95  96                 //将所有的选择符拼接 97                 i = groups.length; 98                 while ( i-- ) { 99                     groups[i] = nid + toSelector( groups[i] );100                 }101                 //探测是否有a+div或者[class~='abc']类似的情况,rsibling = /[+~]/102                 newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;103                 newSelector = groups.join(",");104             }105             //newSelector存在的话106             if ( newSelector ) {107                 try {108                     //直接在context下用querySelectorAll,并将结果压入数组109                     push.apply( results,110                         newContext.querySelectorAll( newSelector )111                     );112                     return results;113                 } catch(qsaError) {114                 } finally {115                     //最后删除添加的id116                     if ( !old ) {117                         context.removeAttribute("id");118                     }119                 }120             }121         }122     }123 124     // 其它情况都用select,这个函数以后分析125     return select( selector.replace( rtrim, "$1" ), context, results, seed );126 }

接下来就是对里面那些相关函数的分析,这里面的涉及到的函数或变量主要有:setDocument,documentIsHTML,contains,toSelector,testContext,rbuggyQSA,tokenize

rbuggyQSA,tokenize,setDocument因为太绕(我连开5,6个jquery一起读了,然后再用chrome来追踪才读出来的,泪奔。。。),所以这次先不讲了。

首先是documentIsHTML,废话不多说,直接上代码:

1 documentIsHTML = !isXML( doc );
1 isXML = Sizzle.isXML = function( elem ) {2     // 首先是确定根元素是否有documentElement3     // 然后是确定documentElement节点名字是不是HTML4     var documentElement = elem && (elem.ownerDocument || elem).documentElement;5     return documentElement ? documentElement.nodeName !== "HTML" : false;6 };

然后是contains,contains是在setDocument中定义的,因为contains是Sizzle中的全局变量,所以就可以到处用了。

1     // rnative = /^[^{]+\{\s*\[native \w/用于检测是否有原生函数 2     // docElem = 根元素.documentElement 3     // hasCompare = rnative.test( docElem.compareDocumentPosition ); 4     contains = hasCompare || rnative.test( docElem.contains ) ? 5         //首先是有compareDocumentPosition或者contains的情况 6         function( a, b ) { 7             var adown = a.nodeType === 9 ? a.documentElement : a, 8                 bup = b && b.parentNode; 9             //先判断是否a直接包含b.parentNode10             //否则,在确定bup后,先尝试用contains11             //再用compareDocumentPosition12             //compareDocumentPosition返回16表示a包含bup13             //故要用&16将其转化为0或非0数字14             return a === bup || !!( bup && bup.nodeType === 1 && (15                 adown.contains ?16                     adown.contains( bup ) :17                     a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 1618             ));19         } :20         //b循环向上取parent,若与a相同,则返回true21         function( a, b ) {22             if ( b ) {23                 while ( (b = b.parentNode) ) {24                     if ( b === a ) {25                         return true;26                     }27                 }28             }29             //循环不到,就返回false30             return false;31         };

然后是toSelector:

1 //循环,将每个对象下的value值合并到selector 2 function toSelector( tokens ) { 3     var i = 0, 4         len = tokens.length, 5         selector = ""; 6     for ( ; i < len; i++ ) { 7         selector += tokens[i].value; 8     } 9     return selector;10 }

对于selector有空格的情况,toknize函数解析的时候会将空格也独立解析成一项,所以在这边合并时空格仍旧会出现,不会被丢弃。

最后是testContext,检测context是否为空,并且context下是否有getElementsByTagName方法,代码如下:

1 function testContext( context ) {2     return context && typeof context.getElementsByTagName !== strundefined && context;3 }

呼,搞定~

转载于:https://www.cnblogs.com/cyITtech/p/3598410.html

你可能感兴趣的文章
Android设计模式实战---责任链模式
查看>>
剑指Offer_31_整数中1出现的次数(从1到n整数中1出现的次数)
查看>>
10月29日 迅雷会员vip账号分享 91freevip 晚间21:00更新
查看>>
【一题多解】Python 字符串逆序
查看>>
open ball、closed ball 与 open set、closed set(interior point,limit point)、dense set
查看>>
字典(dictionary)与映射(map)
查看>>
Python 编程规范 —— TODO 注释(结合 PyCharm)
查看>>
十万个为什么 —— 名词解释(体育)
查看>>
table的设置(w3c)
查看>>
冲刺一
查看>>
【练习】在一个字符串中找到第一个只出现一次的字符。如输入abaccdeff,则输出b...
查看>>
sql 语句
查看>>
VUE一 基础语法
查看>>
[MySQl]MySQL忘记密码
查看>>
Android的minSdkVersion,targetSdkVersion,maxSdkVersion
查看>>
Xceed WinForm数据表格控件Xceed Grid For .NET控件详细介绍及下载地址
查看>>
linux 下连接mysql服务器
查看>>
Linux常用网络命令整理
查看>>
C++ 面向对象
查看>>
Maven Nexus
查看>>