JQuery selector source code explanation (5): tokenize parsing process, jquerytokenize

Source: Internet
Author: User

JQuery selector source code explanation (5): tokenize parsing process, jquerytokenize

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:

Http://www.bkjia.com/article/63155.htm
Http://www.bkjia.com/article/63163.htm

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.
Copy codeThe Code is as follows:
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 = matchexpr1_type.exe c (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.
Copy codeThe Code is as follows:
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.
Copy codeThe Code is as follows:
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.
Copy codeThe Code is as follows:
If (match = matchexpr1_type.exe c (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.
Copy codeThe Code is as follows:
Matched = match. shift ();

Remove the first element div in match and assign it to the matched variable. In this case, matched = "div", match = ["div"]

Copy codeThe Code is as follows:
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.
Copy codeThe Code is as follows:
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.

Copy codeThe Code is as follows:
If (match = matchexpr1_type.exe c (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.
Copy codeThe Code is as follows:
If (match = matchexpr1_type.exe c (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:
Copy codeThe Code is as follows:
Match = preFilters [type] (match)

The preFilters ["PSEUDO"] Code is as follows:

Copy codeThe 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:
Copy codeThe Code is as follows:
[": Not (. class: contain ('span '): eq (3) "," not ",". class: contain ('span '): eq (3 ", undefined

Copy codeThe Code is as follows:
Unquoted =! Match [5] & match [2]

Unquoted = ". class: contain ('span '): eq (3"

Copy codeThe Code is as follows:
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.
Copy codeThe Code is as follows:
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.

Copy codeThe Code is as follows:
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:
Copy codeThe Code is as follows:
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:

Copy codeThe Code is as follows:
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.
Copy codeThe Code is as follows:
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.
Copy codeThe Code is as follows:
If (match = matchexpr1_type.exe c (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.
Copy codeThe Code is as follows:
Matched = match. shift ();

Remove the first element class in match and assign it to the matched variable. In this case, matched = "class", match = ["class"]

Copy codeThe Code is as follows:
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.
Copy codeThe Code is as follows:
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.

Copy codeThe Code is as follows:
If (match = matchexpr1_type.exe c (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:
Copy codeThe Code is as follows:
Match = preFilters [type] (match)

The preFilters ["PSEUDO"] Code is shown above, which is not listed here.

Copy codeThe 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:
[": Contain ('span ')", "contain", "'span", "'", "span", undefined, undefined]

Copy codeThe Code is as follows:
Unquoted =! Match [5] & match [2];

Unquoted = "span"
Copy codeThe Code is as follows:
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.
Copy codeThe Code is as follows:
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].

Copy codeThe Code is as follows:
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"

Copy codeThe Code is as follows:
Matched = match. shift ();

Remove the match array from ": contain ('span ')" and assign the matched variable

Copy codeThe Code is as follows:
Tokens. push ({
Value: matched,
Type: type,
Matches: match
}


Create a new object {value:
": Contain ('span ')", type: "PSEUDO", matches: ["contain", "span"]}, and press this object into the tokens array.
Copy codeThe Code is as follows:
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.
Copy codeThe Code is as follows:
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

 Copy codeThe Code is as follows:
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
 Copy codeThe Code is as follows:
Excess = unquoted. indexOf (")", unquoted. length-excess)-unquoted. length
 

Calculated: not selector end position (that is, the right bracket position) 22
Copy codeThe Code is as follows:
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 ')"
Copy codeThe Code is as follows:
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')"]
Copy codeThe Code is as follows:
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 ')"]
Copy codeThe Code is as follows:
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.
Copy codeThe Code is as follows:
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"]}
Copy codeThe Code is as follows:
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.

Related Article

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.