Home  >  Article  >  Web Front-end  >  jQuery selector source code interpretation (8): addCombinator function_jquery

jQuery selector source code interpretation (8): addCombinator function_jquery

WBOY
WBOYOriginal
2016-05-16 16:06:541023browse

function addCombinator(matcher, combinator, base)

1、源码

复制代码 代码如下:

function addCombinator(matcher, combinator, base) {
 var dir = combinator.dir, checkNonElements = base
   && dir === "parentNode", doneName = done ;

 return combinator.first ?
 // Check against closest ancestor/preceding element
 function(elem, context, xml) {
  while ((elem = elem[dir])) {
   if (elem.nodeType === 1 || checkNonElements) {
    return matcher(elem, context, xml);
   }
  }
 } :

 // Check against all ancestor/preceding elements
 function(elem, context, xml) {
  var data, cache, outerCache, dirkey = dirruns " " doneName;

  // We can't set arbitrary data on XML nodes, so they don't
  // benefit from dir caching
  if (xml) {
   while ((elem = elem[dir])) {
    if (elem.nodeType === 1 || checkNonElements) {
     if (matcher(elem, context, xml)) {
      return true;
     }
    }
   }
  } else {
   while ((elem = elem[dir])) {
    if (elem.nodeType === 1 || checkNonElements) {
     outerCache = elem[expando] || (elem[expando] = {});
     if ((cache = outerCache[dir])
       && cache[0] === dirkey) {
      if ((data = cache[1]) === true
        || data === cachedruns) {
       return data === true;
      }
     } else {
      cache = outerCache[dir] = [ dirkey ];
      cache[1] = matcher(elem, context, xml)
        || cachedruns;
      if (cache[1] === true) {
       return true;
      }
     }
    }
   }
  }
 };
}

2、功能

生成关系选择器的执行函数。

3、参数

matcher——位置关系前连续的过滤选择器匹配函数数组,该函数用于匹配通过位置关系获得的节点是否符合选择器要求。在实际执行过程中,该函数可能是关系选择器前已生成的elementMatcher(matchers)。例如:div.map>span,在Sizzle编译遇到>时,会将div.map的编译函数作为第一个参数调用addCombinator函数,用以检查获取的span父节点是否满足div.map这两个条件。

combinator——关系选择器对应Expr.relative中的值,Expr.relative中各种关系选择器的值如下。使用该参数的first属性来确定返回的是仅检查紧邻对象的函数还是遍历所有可能对象的函数。将通过如下代码:elem = elem[dir],获取指定位置关系的节点,其中dir等于combinator.dir。

复制代码 代码如下:

Expr.relative : {
 ">" : {
  dir : "parentNode",
  first : true
 },
 " " : {
  dir : "parentNode"
 },
 " " : {
  dir : "previousSibling",
  first : true
 },
 "~" : {
  dir : "previousSibling"
 }
}

base - This parameter, together with combinator.dir, determines the value of the variable checkNonElement. The code is as follows. This value is literally understood to mean that the current check is a non-DOM element, that is, when elem.nodeType!=1, if the value is true, the matching function will be executed, otherwise the loop will end.

4. Return function

4.1 If the relationship selector is > or , the following function is returned:

Copy code The code is as follows:

function(elem, context, xml) {
while ((elem = elem[dir])) {
if (elem.nodeType === 1 || checkNonElements) {
Return matcher(elem, context, xml);
}
}
}

4.1.1 Function
If the element type node is checked (i.e. checkNonElements==false), iteratively obtain the first element type node of the elem specified position relationship (elem.nodeType == 1), execute the matching function, check whether the node meets the requirements, and return true if it meets the requirements. , otherwise return false;

If all types of nodes are checked (i.e. checkNonElements==true), get the adjacent nodes with the specified position relationship of elem, execute the matching function, check whether the node meets the requirements, return true if it meets the requirements, otherwise return false;
Some people may ask, isn't it said that it is a close neighbor relationship? So why does the process of iterative acquisition appear in the code? This is because some browsers will regard the line breaks between node texts as TextNode, so during processing, these nodes need to be skipped until the next element node.
4.1.2 Parameters
elem – the single node element to be checked.

context - the context node that performs the entire selector string matching, most of the time it is useless.

xml——Is the current search object an HTML or XML document? If it is HTML, the xml parameter is false.

4.2 If the relationship selector is ~ or space, the following function will be returned:

Copy code The code is as follows:

//Check against all ancestor/preceding elements
function(elem, context, xml) {
var data, cache, outerCache, dirkey = dirruns " " doneName;

// We can't set arbitrary data on XML nodes, so they don't
// benefit from dir caching
if (xml) {
while ((elem = elem[dir])) {
if (elem.nodeType === 1 || checkNonElements) {
If (matcher(elem, context, xml)) {
Return true;
}
}
}
} else {
while ((elem = elem[dir])) {
if (elem.nodeType === 1 || checkNonElements) {
outerCache = elem[expando] || (elem[expando] = {});
If ((cache = outerCache[dir])
&& cache[0] === dirkey) {
If ((data = cache[1]) === true
|| data === cachedruns) {
        return data === true;
}
} else {
cache = outerCache[dir] = [dirkey];
cache[1] = matcher(elem, context, xml)
|| cachedruns;
If (cache[1] === true) {
        return true;
}
}
}
}
}
};

4.2.1 Function

If you are checking an XML document, the process is the same as the return function in 4.1. See the code within the curly brackets in if (XML) { ... } in the above code.

If it is an HTML document, match the current element according to the matcher. If the match is successful, return true; otherwise, return false.

4.2.2 Parameters
elem – the single node element to be checked.

context - the context node that performs the entire selector string matching, most of the time it is useless.

xml——Is the current search object an HTML or XML document? If it is HTML, the xml parameter is false.

4.2.3 Code Description

Internal variables

dirkey - the key used to cache node detection results. During an execution, if a node is checked, the detection result (true or false) will be recorded in the node's dirkey attribute (the attribute name is the value of dirkey). Then during this execution, if it is encountered again When reaching this node, there is no need to detect it again. The reason why caching is needed is because multiple nodes will have the same parent node or sibling node. Using caching can reduce the number of detections and improve performance.

dirruns - Each time the precompiled code organized by matcherFromGroupMatchers is executed, a pseudo-random number will be generated to distinguish different execution processes.
doneName——Every time the addCombinator function is executed, the done variable will be incremented by 1 to distinguish the different generated position relationship matching functions.

cachedruns - used to record which DOM element this match is. For example: div.map>span, there are 3 elements that match the span selector, then when the > matching function is executed for each element, the cachedruns are 0, 1, and 2 in order. According to the code, the role of cachedruns can be directly understood as during an execution process, when using elementMatchers for the same element for matching, when the same element is encountered again, the unmatched result can be obtained directly from This is what happens. If anyone encounters it, please let me know, thank you!

Code explanation

Copy code The code is as follows:

while ((elem = elem[dir])) {
if (elem.nodeType === 1 || checkNonElements) {
// If the expando attribute of the elem node does not exist, give it an empty object and give it to outerCache
at the same time. // If the expando attribute of the elem node exists, assign its value to outerCache
outerCache = elem[expando] || (elem[expando] = {});
/*
* If outCache[dir] has a value and its first element is equal to the current dirkey,
* It means that the current position selector has detected the node during this execution, executed the statement in the if, and obtained the result directly from the cache
* If outCache[dir] does not exist, or the first element is not equal to the current dirkey,
* * It means that the current position selector has not detected the node during this execution, executes the statement in else, matches the node and puts the result into the cache
*/
if ((cache = outerCache[dir])
&& cache[0] === dirkey) {
// If the detection result in the cache is equal to true or the value of cachedruns, the detection result will be returned (either false or true),
// Otherwise, continue to loop to obtain the previous node that matches the position relationship for matching
if ((data = cache[1]) === true
|| data === cachedruns) {
Return data === true;
}
} else {
//Assign the array [dirkey] to outerCache[dir] and cache
cache = outerCache[dir] = [ dirkey ];
// If the match is successful, assign true to cache[1], otherwise assign the value of cachedruns to cache[1]
cache[1] = matcher(elem, context, xml)
|| cachedruns;
// If the matching result is true, return true, otherwise continue to loop to obtain the previous node that matches the position relationship for matching
if (cache[1] === true) {
Return true;
}
}
}
}
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn