jQuery源码学习笔记八

今天把jQuery的Sizzle选择器引擎讲完。最后给出其大体的工作流程。这东西非常复杂,不要妄图看一遍就明白了。无论看懂与否,多看点源码,还是有裨益的。至少在处理循环结构上有收获吧。

 

// EXPOSE
jQuery.find = Sizzle;
jQuery.filter = Sizzle.filter;
jQuery.expr = Sizzle.selectors;
//以:开头许多都是自定义伪类
jQuery.expr[":"] = jQuery.expr.filters;
//css属性display引起的元素不可见
Sizzle.selectors.filters.hidden = function(elem){
  return elem.offsetWidth === 0 || elem.offsetHeight === 0;
};
//css属性display引起的元素不可见
Sizzle.selectors.filters.visible = function(elem){
  return elem.offsetWidth > 0 || elem.offsetHeight > 0;
};
//是否在运动中
Sizzle.selectors.filters.animated = function(elem){
  return jQuery.grep(jQuery.timers, function(fn){
    return elem === fn.elem;
  }).length;
};
//重载jQuery.multiFilter
jQuery.multiFilter = function( expr, elems, not ) {
  if ( not ) {
    expr = ":not(" + expr + ")";
  }

  return Sizzle.matches(expr, elems);
};
//把路径上的元素放到结果上,dir为parentNode,previousSibling,nextSilbing
jQuery.dir = function( elem, dir ){
  var matched = [], cur = elem[dir];
  while ( cur && cur != document ) {
    if ( cur.nodeType == 1 )
      matched.push( cur );
    cur = cur[dir];
  }
  return matched;
};
//在内部调用result好像都为2,dir为previousSibling,nextSilbing
//用于子元素过滤
jQuery.nth = function(cur, result, dir, elem){
  result = result || 1;
  var num = 0;
  //如果cur为undefined中止循环
  for ( ; cur; cur = cur[dir] )
    if ( cur.nodeType == 1 && ++num == result )
      break;

  return cur;
};
//查找不等于elem的兄弟元素节点
jQuery.sibling = function(n, elem){
  var r = [];

  for ( ; n; n = n.nextSibling ) {
    if ( n.nodeType == 1 && n != elem )
      r.push( n );
  }

  return r;
};

return;

window.Sizzle = Sizzle;

 

好了,回头看Sizzle的主程序部分:

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;//按照ID NAME TAG的优先级顺序执行
       //这里可以想象一下
       //match = "#aaa".exec( /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/)
       //然后检测match是否为空数组,空数组相当于false
        if ( (match = Expr.match[ type ].exec( expr )) ) {
         //ID的正则 /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/
            var left = RegExp.leftContext
            //如果不是一步到位,是复杂的表达式,需要多次查找与筛选
            if ( left.substr( left.length - 1 ) !== "\\" ) {
              //把换行符去掉,得到正常的字段
              //如"#id12\
              //34"
              //去掉后,就得到"#id1234"
                match[1] = (match[1] || "").replace(/\\/g, "");
                set = Expr.find[ type ]( match, context, isXML );
                if ( set != null ) {
                  //移除相应部分的表达,
                  // 如#aaa ee,得到ID对应的元素后,把#aaa去掉,
                  //然后用Expr的表达式来匹配剩下的部分
                    expr = expr.replace( Expr.match[ type ], "" );
                    break;
                }
            }
        }
    }
 
    if ( !set ) {
        //返回所有后代
        set = context.getElementsByTagName("*");
    }
 
    return {//返回一个对象
        set: set,
        expr: expr
    };
};
Sizzle.filter = function(expr, set, inplace, not){
  var old = expr, result = [], curLoop = set, match, anyFound,
  isXMLFilter = set && set[0] && isXML(set[0]);
 
  while ( expr && set.length ) {
    for ( var type in Expr.filter ) {
      //这是Expr.filter中的键值对
      //PSEUDO: function(elem, match, i, array){},
      //CHILD: function(elem, match){},
      //ID: function(elem, match){},
      //TAG: function(elem, match){},
      //CLASS: function(elem, match){},
      //ATTR: function(elem, match){},
      //POS: function(elem, match, i, array){}
      if ( (match = Expr.match[ type ].exec( expr )) != null ) {//match为数组
        var filter = Expr.filter[ type ], found, item;//filter这函数
        anyFound = false;
 
        if ( curLoop == result ) {//如果结果集为空数组,就让result = [];
          result = [];
        }
 
        if ( Expr.preFilter[ type ] ) {
          //这是Expr.preFilter中的键值对
          //CLASS: function(match, curLoop, inplace, result, not, isXML){},
          //ID: function(match){},
          //TAG: function(match, curLoop){},
          //CHILD: function(match){ },
          //ATTR: function(match, curLoop, inplace, result, not, isXML){},
          //PSEUDO: function(match, curLoop, inplace, result, not){ },
          //POS: function(match){}
          //preFilter与filter的功能不同,preFilter对字符串进行调整,好让选择器能找到元素
          //filter对查找到的元素或元素数组进行筛选
          match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
          if ( !match ) {//如果返回的是false
            anyFound = found = true;//就把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 ) {
                result.push( item );//符合要求就放到结果数组中
                anyFound = true;
              }
            }
          }
        }
 
        if ( found !== undefined ) {
          if ( !inplace ) {
            curLoop = result;//结果数组将作为一下次要遍历的元素集合返回
          }
          //移除用户输入字符串已查找了的那一部分表达式
          expr = expr.replace( Expr.match[ type ], "" );
 
          if ( !anyFound ) {
            return [];
          }
 
          break;
        }
      }
    }
 
    // Improper expression
    if ( expr == old ) {
      if ( anyFound == null ) {
        throw "Syntax error, unrecognized expression: " + expr;
      } else {
        break;
      }
    }
 
    old = expr;
  }
 
  return curLoop;
};

主程序:

var Sizzle = function(selector, context, results, seed) {
  results = results || [];
  context = context || document;
 
  if ( context.nodeType !== 1 && context.nodeType !== 9 )
    return [];//context必须为DOM元素或document,要不返回空数组
 
  if ( !selector || typeof selector !== "string" ) {
    return results;//selector必须存在并且为字符串,否则返回上次循环的结果集
  }
 
  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;//匹配内容的右边归入extra
      break;
    }
  }
  //POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
  if ( parts.length > 1 && origPOS.exec( selector ) ) {
    //处理E F   E > F    E + F   E ~ F
    if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
      //这里的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 ) {//倒序的while循环比for循环快
      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);
  }
  //数组化NodeList,并加入结果集中
  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 );
    if ( sortOrder ) {
      hasDuplicate = false;
      results.sort(sortOrder);//重排结果集中的DOM元素,按照原来在网页先后顺序排列
      if ( hasDuplicate ) {
        for ( var i = 1; i < results.length; i++ ) {//确保没有重复的DOM元素,方法比较垃圾
          if ( results[i] === results[i-1] ) {
            results.splice(i--, 1);
          }
        }
      }
    }
  }
 
  return results;
};