JQuery selector Code Description (5) -- describes the tokenize parsing process, jquerytokenize

Source: Internet
Author: User

JQuery selector Code Description (5) -- describes the tokenize parsing process, jquerytokenize

For Original Articles, please indicate the source. Thank you!

The following analysis is based on the jQuery-1.10.2.js version.

The following uses $ ("div: not (. class: contain ('span '): eq (3) ") is used as an example to explain how tokenize and preFilter complete parsing. For more information about each line of code in the tokenize method and preFilter class, see the following two articles:

JQuery selector code (3) -- tokenize Method

JQuery selector code (4) -- Expr. preFilter


The following is the source code of the tokenize method. For the sake of simplicity, I have removed all the Code related to cache, comma matching, and link character matching, only the core Code related to the current example is left. The code to be removed is very simple. You can read the above article if necessary.

In addition, the Code is written at the top of the explanatory text.

function tokenize(selector, parseOnly) {var matched, match, tokens, type, soFar, groups, preFilters;soFar = selector;groups = [];preFilters = Expr.preFilter;while (soFar) {if (!matched) {groups.push(tokens = []);}matched = false;for (type in Expr.filter) {if ((match = matchExpr[type].exec(soFar))&& (!preFilters[type] || (match = preFilters[type](match)))) {matched = match.shift();tokens.push({value : matched,type : type,matches : match});soFar = soFar.slice(matched.length);}}if (!matched) {break;}}return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) :tokenCache(selector, groups).slice(0);}

First, tokenize is called by the select method for the first time during jQuery execution, and "div: not (. class: contain ('span '): eq (3) "is passed in as the selector parameter.

<span style="white-space:pre"></span>soFar = selector;
SoFar = "div: not (. class: contain ('span '): eq (3 )"

When you enter the while loop for the first time, because matched has not been assigned a value, execute the following statement body in the if statement. This statement will initialize the tokens variable and press tokens into the groups array.

<span style="white-space:pre"></span>groups.push(tokens = []);

Then, enter the for statement.

First for loop: extract the first element "TAG" from Expr. filter and assign it to the type variable to execute the loop body code.

if ((match = matchExpr[type].exec(soFar))&& (!preFilters[type] || (match = preFilters[type](match)))) {

The execution result of match = matchexpr1_type.exe c (soFar) is as follows:

Match = ["div", "div"]

The first selector in the example is div, which matches the Regular Expression of matchExpr ["TAG"] and does not exist in preFilters ["TAG"]. Therefore, the if statement body is executed.

<span style="white-space:pre"></span>matched = match.shift();
Remove the first element div in match and assign it to the matched variable. In this case, matched = "div", match = ["div"]

tokens.push({value : matched,type : type,matches : match}
Create a new object {value: "div", type: "TAG", matches: ["div"]} and press the object into the tokens array.

soFar = soFar.slice(matched.length);
When the soFar variable deletes the div, soFar = ": not (. class: contain ('span '): eq (3 )"

The second for loop: extract the second element "CLASS" from Expr. filter and assign it to the type variable to execute the loop body code.

if ((match = matchExpr[type].exec(soFar))&& (!preFilters[type] || (match = preFilters[type](match)))) {
This loop ends because the current soFar = ": not (. class: contain ('span '): eq (3)" does not match the regular expression of the CLASS type.

Third for loop: extract the third element "ATTR" from Expr. filter and assign it to the type variable to execute the loop body code.

Similarly, because the current residual selector is not a property selector, this cycle ends.

The fourth for loop: extract the fourth element "CHILD" from Expr. filter and assign it to the type variable to execute the loop body code.

Similarly, the cycle ends because the current residual selector is not a CHILD selector.

The fifth for loop: extract the fifth element "PSEUDO" from Expr. filter and assign it to the type variable to execute the loop body code.

if ((match = matchExpr[type].exec(soFar))&& (!preFilters[type] || (match = preFilters[type](match)))) {
The execution result of match = matchexpr1_type.exe c (soFar) is as follows:

[": Not (. class: contain ('span '): eq (3) "," not ",". class: contain ('span '): eq (3 ", undefined]

Because preFilters ["PSEUDO"] exists, execute the following code:

<span style="white-space:pre"></span>match = preFilters[type](match)
The preFilters ["PSEUDO"] Code is as follows:

"PSEUDO" : function(match) {var excess, unquoted = !match[5] && match[2];if (matchExpr["CHILD"].test(match[0])) {return null;}if (match[3] && match[4] !== undefined) {match[2] = match[4];} else if (unquoted&& rpseudo.test(unquoted)&& (excess = tokenize(unquoted, true))&& (excess = unquoted.indexOf(")", unquoted.length- excess)- unquoted.length)) {match[0] = match[0].slice(0, excess);match[2] = unquoted.slice(0, excess);}return match.slice(0, 3);}
The input match parameter is equal:

[": Not (. class: contain ('span '): eq (3) "," not ",". class: contain ('span '): eq (3 ", undefined]

<span style="white-space:pre"></span>unquoted = !match[5] && match[2]
Unquoted = ". class: contain ('span '): eq (3"

if (matchExpr["CHILD"].test(match[0])) {return null;}
Match [0] = ": not (. class: contain ('span '): eq (3) ", does not match the matchExpr [" CHILD "] Regular Expression, and does not execute the return null statement.

if (match[3] && match[4] !== undefined) {match[2] = match[4];}
Because match [3] and match [4] are both equal to undefined, the statement body that executes else.

<span style="white-space:pre"></span>else if (unquoted&& rpseudo.test(unquoted)&& (excess = tokenize(unquoted, true))&& (excess = unquoted.indexOf(")", unquoted.length - excess) - unquoted.length)
At this time, unquoted = ". class: contain ('span '): eq (3 ", true, and because unquoted contains: contain ('span'), it matches the regular expression rpseudo, so rpseudo. test (unquoted) is true, and then call tokenize to parse unquoted again, the following statement:

<span style="white-space:pre"></span>excess = tokenize(unquoted, true)
When the tokenize function is called this time, the input selector parameter is equal to ". class: contain ('span '): eq (3", parseOnly is equal to true. The Execution Process in the function body is as follows:

<span></span>soFar = selector;
SoFar = ". class: contain ('span '): eq (3"

When you enter the while loop for the first time, because matched has not been assigned a value, execute the following statement body in the if statement. This statement will initialize the tokens variable and press tokens into the groups array.

<span></span>groups.push(tokens = []);

Then, enter the for statement.

First for loop: extract the first element "TAG" from Expr. filter and assign it to the type variable to execute the loop body code.

if ((match = matchExpr[type].exec(soFar))&& (!preFilters[type] || (match = preFilters[type](match)))) {
Because the current residual selector is not a TAG selector, this cycle ends.

The second for loop: extract the second element "CLASS" from Expr. filter and assign it to the type variable to execute the loop body code.

The execution result of match = matchexpr1_type.exe c (soFar) is as follows:

Match = ["class", "class"]

Because preFilters ["CLASS"] does not exist, execute the if statement body.

<span></span>matched = match.shift();
Remove the first element class in match and assign it to the matched variable. In this case, matched = "class", match = ["class"]
tokens.push({value : matched,type : type,matches : match}
Create a new object {value: "class", type: "CLASS", matches: ["class"]} and press the object into the tokens array.
soFar = soFar.slice(matched.length);
When the soFar variable deletes the class, soFar = ": contain ('span '): eq (3"

Third for loop: extract the third element "ATTR" from Expr. filter and assign it to the type variable to execute the loop body code.

Similarly, because the current residual selector is not a property selector, this cycle ends.

The fourth for loop: extract the fourth element "CHILD" from Expr. filter and assign it to the type variable to execute the loop body code.

Similarly, the cycle ends because the current residual selector is not a CHILD selector.

The fifth for loop: extract the fifth element "PSEUDO" from Expr. filter and assign it to the type variable to execute the loop body code.

if ((match = matchExpr[type].exec(soFar))&& (!preFilters[type] || (match = preFilters[type](match)))) {
The execution result of match = matchexpr1_type.exe c (soFar) is as follows:

[": Contain ('span ')", "contain", "'span", "'", "span", undefined, undefined]

Because preFilters ["PSEUDO"] exists, execute the following code:

<span></span>match = preFilters[type](match)
The preFilters ["PSEUDO"] Code is shown above, which is not listed here.

"PSEUDO" : function(match) {var excess, unquoted = !match[5] && match[2];if (matchExpr["CHILD"].test(match[0])) {return null;}if (match[3] && match[4] !== undefined) {match[2] = match[4];} else if (unquoted&& rpseudo.test(unquoted)&& (excess = tokenize(unquoted, true))&& (excess = unquoted.indexOf(")", unquoted.length- excess)- unquoted.length)) {match[0] = match[0].slice(0, excess);match[2] = unquoted.slice(0, excess);}return match.slice(0, 3);}
The input match parameter is equal:

[": Contain ('span ')", "contain", "'span", "'", "span", undefined, undefined]

<span style="white-space:pre"></span>unquoted = !match[5] && match[2];
Unquoted = "span"

if (matchExpr["CHILD"].test(match[0])) {return null;}
Because ": contain ('span ')" does not match the matchExpr ["CHILD"] Regular Expression, internal statement bodies are not executed.

if (match[3] && match[4] !== undefined) {match[2] = match[4];}
Because match [3] = "'", match [4] = "span", execute the if internal statement body and assign "span" to match [2].

return match.slice(0, 3);

Returns a copy of the first three elements of a match.

Return to the for loop of the tokenize method and continue execution. The variable values are as follows:

Match = [": contain ('span ')", "contain", "span"]

SoFar = ": contain ('span '): eq (3"

<span style="white-space:pre"></span>matched = match.shift();
Remove the match array from ": contain ('span ')" and assign the matched variable

tokens.push({value : matched,type : type,matches : match}
Create a new object {value: ": contain ('span ')", type: "PSEUDO", matches: ["contain", "span"]}, and press the object into the tokens array.

soFar = soFar.slice(matched.length);
SoFar variable deletes ": contain ('span ')". At this time, soFar = "): eq (3)", and then runs the while loop again until the for loop ends, there is no valid selector, so the while loop is exited.

return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) :tokenCache(selector, groups).slice(0);
Because parseOnly = true at this time, return the soFar length of 6 at this time, continue to execute the preFilters ["PSEUDO"] Code

<span></span>else if (unquoted&& rpseudo.test(unquoted)&& (excess = tokenize(unquoted, true))&& (excess = unquoted.indexOf(")", unquoted.length - excess) - unquoted.length)
Assign 6 to the excess variable, and then the code
<span style="white-space:pre"></span>excess = unquoted.indexOf(")", unquoted.length - excess) - unquoted.length

Calculated: not selector end position (that is, the right bracket position) 22

match[0] = match[0].slice(0, excess);match[2] = unquoted.slice(0, excess);
Calculate the complete: not selector string (match [0]) and the string in the brackets (match [2]), which are equal:

Match [0] = ": not (. class: contain ('span '))"

Match [2] = ". class: contain ('span ')"

<span style="white-space:pre"></span>return match.slice(0, 3);
Returns a copy of the first three elements in the match statement.

Return to the tokenize function, and match = [": not (. class: contain ('span ')", "not", ". class: contain ('span')"]

<span></span>matched = match.shift();
Remove the first element in match ": not (. class: contain ('span ') ", and assign this element to the matched variable. In this case, matched =" ": not (. class: contain ('span '))"",

Match = ["not", ". class: contain ('span ')"]

tokens.push({value : matched,type : type,matches : match}
Create a new object {value: ": not (. class: contain ('span ') "", type: "PSEUDO", matches: ["not ",". class: contain ('span ') "]}, and press the object into the tokens array. In this case, tokens has two elements: div and not selector.

soFar = soFar.slice(matched.length);
SoFar variable deletion ": not (. class: contain ('span ') ", at this time, soFar =": eq (3) ", after the for loop ends, return to the while loop again, the same way, obtain the eq selector, the third element of tokens. The process is the same as that of not. The final groups result is as follows:

Group [0] [0] = {value: "div", type: "TAG", matches: ["div"]}

Group [0] [1] = {value: ": not (. class: contain ('span ') ", type:" PSEUDO ", matches: [" not ",". class: contain ('span ') "]}

Group [0] [2] = {value: ": eq (3)", type: "PSEUDO", matches: ["eq", "3"]}

return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) :tokenCache(selector, groups).slice(0);
Because parseOnly = undefined, tokenCache (selector, groups). slice (0) is executed. This statement pushes the groups into the cache and returns its copy.

As a result, after all the parsing is completed, someone may ask, the second element is not parsed here. Yes, this needs to be parsed again in actual operation. Of course, if you want to resolve this issue. "class: contain ('span '): eq (3", save the results of the valid selector to the cache, so you can avoid re-parsing and improve the execution speed. However, this only increases the current running speed. During the execution process, when ". class: contain ('span ')" is submitted for resolution again, it will be saved to the cache.


So far, the entire execution process has been completed.


Dear friends, if you think it's a good job, help me with it and give me some motivation. Thank you!


JQuery selector code (1) -- Sizzle Method

JQuery selector code (2) -- select Method

JQuery selector code (3) -- tokenize Method

JQuery selector code (4) -- Expr. preFilter

JQuery selector Code Description (5) -- describes the tokenize parsing process

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.