jQuery.API源码深入剖析以及应用实现(3) - 选择器篇(上)

xiaoxiao2022-06-11  27

还漏了一个框题,jQuery的冲突机制解决方法jQuery.noConflict()以及jQuery.noConflict(extreme),这里先分析一下:

jQuery.noConflict():运行这个函数将变量$的控制权让渡给第一个实现它的那个库。

jQuery.noConflict(extreme):将$和jQuery的控制权都交还给原来的库。

比如在prototype框架中的$会和jQuery框架中的$产生命名冲突,这里就是为了解决这种问题。

现在先看下noConflict方法的具体实现:

noConflict:  function ( deep ) {     window.$  =  _$;       if  ( deep )         window.jQuery  =  _jQuery;       return  jQuery; }

其中_$,_jQuery是在jQquery源码的开始几行定义的:

( function (){  var           //       _jQuery  =  window.jQuery,     _$  =  window.$,         //   })();

他们都是为了防止$被覆盖而将window.jQuery,window.$放在临时变量中保存起来。

当deep为空的时候,“_$”覆盖“window.$”,“$”的常规功能失效,但jQuery还可以继续使用。当有新的库中重新定义“$”的时候,“jQuery”继续为jQquery的常规功能,而“$”就不是jQuery中的了,它是属于新的库的常规功能;

当deep不为空的时候,它将“_jQuery”覆盖“window.jQuery”,这样导致可能jQuery插件失效;另外方法返回的jQuery,实际上没有被覆盖。通过它完全可以移到新的一个命名空间,如dom.query = jQuery.noConflict(true); dom.query("div p").hide();

前言

这篇文章将介绍jQuery选择器的原理,主要内容包括:

分析

一、基本

1. 【#id】和【element】

 在第一篇中曾经提到核心函数的概念,形如$("#result")【jQuery(expression,[context])】表达式,归根调用【jQuery(elements)】,因此将调用:

if  ( selector.nodeType ) {      this [ 0 =  selector;      this .length  =  1 ;      this .context  =  selector;      return  this ; }

 

2. 【.class】

在第一篇中曾经提到核心函数的概念,形如 $(".container") 【jQuery(expression,[context])】表达式字符串的实现。

3. 【*】

通过第一篇的结论,将代码进行到:

Sizzle.find  =  function (expr, context, isXML){      var  set, match;       if  (  ! expr ) {          return  [];     }       for  (  var  i  =  0 , l  =  Expr.order.length; i  <  l; i ++  ) {          var  type  =  Expr.order[i], match;          if  ( (match  =  Expr.match[ type ].exec( expr )) ) {              var  left  =  RegExp.leftContext;               if  ( left.substr( left.length  -  1  )  !==  " "" "  ) {                 match[ 1 =  (match[ 1 ||  "" ).replace( / "" / g,  "" );                 set  =  Expr.find[ type ]( match, context, isXML );                   if  ( set  !=  null  ) {                     expr  =  expr.replace( Expr.match[ type ],  ""  );                      break ;                 }             }         }     }       if  (  ! set ) {         set  =  context.getElementsByTagName( " * " );     }       return  {set: set, expr: expr}; };

当expr为“*”的时候,根据Expr.match[ type ].exec( expr )为true时,type为TAG,因此将执行:

if ( left.substr( left.length - 1 ) !== """" ) {     match[1] = (match[1] || "").replace(/""/g, "");     set = Expr.find[ type ]( match, context, isXML );      if ( set != null ) {         expr = expr.replace( Expr.match[ type ], "" );         break;     } }

继续查看Expr.find[ type ]的方法,具体实现如下:

var  Expr  =  Sizzle.selectors  =  {    //   find: {     ID:  function (match, context, isXML){          if  (  typeof  context.getElementById  !==  " undefined "  &&  ! isXML ) {              var  m  =  context.getElementById(match[ 1 ]);              return  m  ?  [m] : [];         }     },     NAME:  function (match, context, isXML){          if  (  typeof  context.getElementsByName  !==  " undefined "  &&  ! isXML ) {              return  context.getElementsByName(match[ 1 ]);         }     },     TAG:  function (match, context){          return  context.getElementsByTagName(match[ 1 ]);     } }

所以将调用context.getElementsByTagName("*");返回context中所有的DOM元素。

4. 【selector1,selector2,selectorN】

参考第一篇中的内容,当表达式包含“,”符号的时候,最后也是返回一个jQuery对象。

二、层级

1. 【ancestor descendant】

在给定的祖先元素下匹配所有的后代元素。

HTML代码jQuery代码结果<form>   <label>Name:</label>   <input name="name" />   <fieldset>       <label>Newsletter:</label>       <input name="newsletter" />  </fieldset> </form> <input name="none" />  $("form input")[ <input name="name" />, <input name="newsletter" /> ]

首先“ancestor descendant” 作为一个表达式字符串,根据第一篇中的内容,它将执行:

//  处理 形如 $("div .container")的表达式字符串 else    return  jQuery( context ).find( selector );

接着查看jQuery对象的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 );     } }

由于jQuery.find = Sizzle; 因此查看Sizzle对象的具体实现:

Code

其中set = Sizzle.filter( ret.expr, ret.set );调用Sizzle.filter方法:

Sizzle.filter  =  function (expr, set, inplace, not){      var  old  =  expr, result  =  [], curLoop  =  set, match, anyFound;      while  ( expr  &&  set.length ) {          for  (  var  type  in  Expr.filter ) {              if  ( (match  =  Expr.match[ type ].exec( expr ))  !=  null  ) {                  var  filter  =  Expr.filter[ type ], found, item;                 anyFound  =  false ;                  if  ( curLoop  ==  result ) {                     result  =  [];                 }                  if  ( Expr.preFilter[ type ] ) {                     match  =  Expr.preFilter[ type ]( match, curLoop, inplace, result, not );                      if  (  ! match ) {                         anyFound  =  found  =  true ;                     }  else  if  ( match  ===  true  ) {                          continue ;                     }                 }                  if  ( match ) {                      for  (  var  i  =  0 ; (item  =  curLoop[i])  !=  null ; i ++  ) {                          if  ( item ) {                             found  =  filter( item, match, i, curLoop );                              var  pass  =  not  ^  !! found;                              if  ( inplace  &&  found  !=  null  ) {                                  if  ( pass ) {                                     anyFound  =  true ;                                 }  else  {                                     curLoop[i]  =  false ;                                 }                             }  else  if  ( pass ) { // $("form input")从这里进                                 result.push( item );                                 anyFound  = true ;                             }                         }                     }                 }                  if  ( found  !==  undefined ) {                      if  (  ! inplace ) {                         curLoop  =  result;                     }                     expr  =  expr.replace( Expr.match[ type ],  ""  );                      if  (  ! anyFound ) {                          return  [];                     }                      break ;                 }             }         }         expr  =  expr.replace( / "s*,"s* / "" );          //  Improper expression          if  ( expr  ==  old ) {              if  ( anyFound  ==  null  ) {                  throw  " Syntax error, unrecognized expression:  "  +  expr;             }  else  {                  break ;             }         }         old  =  expr;     }      return  curLoop; };

最关键是在加粗字代码,result.push( item ); anyFound = true; 和 curLoop = result;将匹配的元素加入result中,然后赋值于curLoop。

而方法的最后返回的是curLoop。所匹配的元素就是最后所需要的jQuery对象。

2. 【parent > child】

在给定的父元素下匹配所有的子元素。

HTML代码jQuery代码结果<form>   <label>Name:</label>   <input name="name" />   <fieldset>       <label>Newsletter:</label>       <input name="newsletter" />  </fieldset> </form> <input name="none" />$("form > input")[ <input name="name" /> ]

我们只要查看一下它的核心代码:

relative: {      //      " > " function (checkSet, part, isXML){          //  当part为单词字符时,如$("form > input"),part为“form”          if  (  typeof  part  ===  " string "  &&  ! / "W / .test(part) ) {              part  =  isXML  ?  part : part.toUpperCase();               for  (  var  i  =  0 , l  =  checkSet.length; i  <  l; i ++  ) {                  var  elem  =  checkSet[i];                  if  ( elem ) {                      //  得到elem的父节点                      var  parent  =  elem.parentNode;                      //  如果父节点名称为part值时,在checkSet[i]上赋值父节点,否则赋值false                     checkSet[i]  =  parent.nodeName  ===  part  ?  parent :  false ;                 }             }          //  当part为非单词字符时,如$(".blue > input"),part为“.blue”         }  else  {              for  (  var  i  =  0 , l  =  checkSet.length; i  <  l; i ++  ) {                  var  elem  =  checkSet[i];                  if  ( elem ) {                     checkSet[i]  =  typeof  part  ===  " string "  ?                         elem.parentNode :                         elem.parentNode  ===  part;                 }             }               if  (  typeof  part  ===  " string "  ) {                 Sizzle.filter( part, checkSet,  true  );             }         }     }, }

从这里我们可以得到checkSet的值集合。

2. 【prev + next】

匹配所有紧接在 prev 元素后的 next 元素。next (Selector) :一个有效选择器并且紧接着第一个选择器。

例子

HTML代码jQuery代码结果<form>   <label>Name:</label>   <input name="name" />   <fieldset>       <label>Newsletter:</label>       <input name="newsletter" />  </fieldset> </form> <input name="none" /> $("label + input")[ <input name="name" />, <input name="newsletter" /> ]

只要查看一下它的核心代码:

relative: {      //        " + " function (checkSet, part){           for  (  var  i  =  0 , l  =  checkSet.length; i  <  l; i ++  ) {              var  elem  =  checkSet[i];              if  ( elem ) {                  //  得到elem的前一个节点                  var  cur  =  elem.previousSibling;                  //  当cur的节点类型不为元素节点的时候,继续得到cur的前一个节点,否则循环结束                  while  ( cur  &&  cur.nodeType  !==  1  ) {                     cur  =  cur.previousSibling;                 }                 checkSet[i]  =  typeof  part  ===  " string "  ?                     cur  ||  false  :                     cur  ===  part;             }         }           if  (  typeof  part  ===  " string "  ) {             Sizzle.filter( part, checkSet,  true  );         }     },      //   }

从这里我们可以得到checkSet的值集合。

3. 【prev ~ next】

匹配 prev 元素之后的所有 siblings 元素。

例子

HTML代码jQuery代码结果<form>   <label>Name:</label>   <input name="name" />   <fieldset>       <label>Newsletter:</label>       <input name="newsletter" />  </fieldset> </form> <input name="none" />  <input name="none2" />$("form ~ input")[ <input name="none" />, <input name="none2" />]

只要查看一下它的核心代码:

relative: {      //        " ~ " function (checkSet, part, isXML){           var  doneName  =  " done "  +  (done ++ ), checkFn  =  dirCheck;           if  (  typeof  part  ===  " string "  &&  ! part.match( / "W / ) ) {              var  nodeCheck  =  part  =  isXML  ?  part : part.toUpperCase();             checkFn  =  dirNodeCheck;         }          checkFn( " previousSibling " , part, doneName, checkSet, nodeCheck, isXML);     } }

其中checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); 调用的是dirCheck方法,它的具体实现为:

function  dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {      for  (  var  i  =  0 , l  =  checkSet.length; i  <  l; i ++  ) {          var  elem  =  checkSet[i];          if  ( elem ) {             elem  =  elem[dir];              var  match  =  false ;               while ( elem &&  elem.nodeType ) {                  if  ( elem[doneName] ) {                     match  =  checkSet[ elem[doneName] ];                      break ;                 }                   if  ( elem.nodeType  ===  1  ) {                      if  (  ! isXML )                         elem[doneName]  =  i;                       if  (  typeof  cur  !==  " string "  ) {                          if  ( elem  ===  cur ) {                             match  =  true ;                              break ;                         }                      }  else  if  ( Sizzle.filter( cur, [elem] ).length  >  0  ) {                         match  =  elem;                          break ;                     }                 }                 //  由于这里dir为previousSibling,所以这里利用循环不断得到elem的前一个节点,并且赋值checkSet数组                 elem  =  elem[dir];             }              checkSet[i]  =  match;         }     } }

从这里我们可以得到checkSet的值集合。

三、简单

1. 【:first】,【:last】,【:even】,【:odd】,【 :eq(index) 】,【 :gt(index) 】,【 :lt(index) 】和 【 :not(selector) 】

:first 匹配找到的第一个元素。

:last 匹配找到的最后一个元素。

:even 匹配所有索引值为偶数的元素,从 0 开始计数。

:odd 匹配所有索引值为奇数的元素,从 0 开始计数。

:eq(index) 匹配一个给定索引值的元素。

:gt(index) 匹配所有大于给定索引值的元素。

:lt(index) 匹配所有小于给定索引值的元素。

:not(selector) 去除所有与给定选择器匹配的元素。

例子

HTML代码jQuery代码<table>   <tr><td>Header 1</td></tr>   <tr><td>Value 1</td></tr>   <tr><td>Value 2</td></tr> </table>$("tr:first"),$("tr:last"),$("tr:even"),$("tr:odd"),$("tr:eq(1)"),$("tr:gt(0)")

首先我们看下它的一个正则表达式:

POS:  / :(nth|eq|gt|lt|first|last|even|odd)(?:"(("d*)"))?(?=[^-]|$) / ,

核心代码从Sizzle.filter开始:

让我们先看下var filter = Expr.filter[ type ],的Expr.filter的具体实现,核心代码为:

接着看下Expr.setFilters的具体实现:

setFilters: {     first:  function (elem, i){          return  i  ===  0 ;     },     last:  function (elem, i, match, array){          return  i  ===  array.length  -  1 ;     },     even:  function (elem, i){          return  i  %  2  ===  0 ;     },     odd:  function (elem, i){          return  i  %  2  ===  1 ;     },     lt:  function (elem, i, match){          return  i  <  match[ 3 -  0 ;     },     gt:  function (elem, i, match){          return  i  >  match[ 3 -  0 ;     },     nth:  function (elem, i, match){          return  match[ 3 -  0  ==  i;     },     eq:  function (elem, i, match){          return  match[ 3 -  0  ==  i;     } }

噢,所有的标识 主要在这里判断elem元素在集合中的逻辑位置,并且返回一个布尔值。

接着 match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not );  Expr.prefilter的具体实现,主要核心代码为:

PSEUDO:  function (match, curLoop, inplace, result, not){              if  ( match[ 1 ===  " not "  ) {                                 //  代码1                  if  ( match[ 3 ].match(chunker).length  >  1  ) {                     match[ 3 =  Sizzle(match[ 3 ],  null null , curLoop);                 }  else  {                      var  ret  =  Sizzle.filter(match[ 3 ], curLoop, inplace,  true  ^  not);                      if  (  ! inplace ) {                         result.push.apply( result, ret );                     }                      return  false ;                 }             }  else  if  ( Expr.match.POS.test( match[ 0 ] ) ) {                  return  true ;             }              return  match;         }

当match[1]匹配中包含为“not”时,即表达式字符串中包含:not时,发生“代码1”;否则,根据POS的正则表达式判断返回true。

最后将匹配的item元素入栈,即 result.push( item )。Sizzle.filter最后返回的是curLoop。所匹配的元素就是最后所需要的jQuery对象。

转载请注明原文地址: https://www.6miu.com/read-4930204.html

最新回复(0)