jquery Selector Source code interpretation (V): tokenize analytical Process _jquery

Source: Internet
Author: User

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

The following is an example of $ ("Div:not (. Class:contain (' span ')): eq (3)") to illustrate how the Tokenize and prefilter sections of code coordinate to complete the parsing. For a detailed explanation of each line of code for the Tokenize method and the Prefilter class, see the following two articles:

Http://www.jb51.net/article/63155.htm
Http://www.jb51.net/article/63163.htm

The following is the source of the Tokenize method, for the sake of simplicity, I removed all the code about caching, matching commas, and matching the relationship characters, leaving only the core code related to the current example. The removed code is very simple, if you need to see the above article can be.

In addition, the code is written uniformly above the explanatory text.

Copy Code code 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 = 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, the tokenize is invoked for the first time by the Select method during jquery execution, and the Div:not (. Class:contain (' span '): EQ (3) "is passed in as the selector parameter.
Copy Code code as follows:

Sofar = selector;

Sofar = "Div:not (. Class:contain (' span ')): eq (3)"
The first time you enter the while loop, because matched has not yet been assigned, execute the following body of the statement within the IF, which initializes the tokens variable and presses the tokens into the groups array.

Copy Code code as follows:

Groups.push (tokens = []);

After that, enter the for statement.

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

Copy Code code as follows:

if (match = Matchexpr[type].exec (Sofar))
&& (!prefilters[type) | | (match = Prefilters[type]
(match))) {

The results of match = Matchexpr[type].exec (Sofar) are as follows:

Match =["div", "div"]

The first selector for the example is a div that matches the regular expression of the matchexpr["tag" and does not exist prefilters["tag", so it executes the statement body within the IF.

Copy Code code as follows:

matched = Match.shift ();

Removes the first element div in match and assigns the element to the matched variable, at which point matched= "div", match = ["Div"]

Copy Code code as follows:

Tokens.push ({
Value:matched,
Type:type,
Matches:match
}

Creates a new object {value: "div", type: "TAG", Matches: ["div"]}, and presses the object into the tokens array.

Copy Code code as follows:

Sofar = Sofar.slice (matched.length);

Sofar variable Delete div, at this point, sofar= ": Not (. Class:contain (' span ')): eq (3)"
Second for loop: Remove the second element "CLASS" from the Expr.filter to the type variable and execute the loop body code.

Copy Code code as follows:

if (match = Matchexpr[type].exec (Sofar))
&& (!prefilters[type) | | (match = Prefilters[type]
(match))) {

End this loop because the current sofar= ": Not (. Class:contain (' span ')): eq (3)" does not match the regular expression of class type.
Third for loop: Remove the third element "ATTR" from the Expr.filter and assign it to the type variable, executing the loop body code.
Also, because the current remaining selector is not a property selector, it ends the loop.

Fourth time for loop: Remove the fourth element "child" from Expr.filter and assign it to the type variable, executing the loop body code.
Similarly, because the current remaining selector is not a child selector, the loop is ended.

Fifth time for loop: Remove the Fifth Element "PSEUDO" from Expr.filter and assign it to the type variable, executing the loop body code.

Copy Code code as follows:

if (match = Matchexpr[type].exec (Sofar))
&& (!prefilters[type) | | (match = Prefilters[type]
(match))) {

The results of match = Matchexpr[type].exec (Sofar) are as follows:
[': Not (. Class:contain (' span ')]: eq (3) "," not ",". Class:contain (' span ')): eq (3 ", Undefined, undefined, undefined, Undefined, undefined, undefined, undefined, undefined]

Because there is a prefilters["PSEUDO", the following code is executed:

Copy Code code as follows:

Match = Prefilters[type] (match)

The prefilters["PSEUDO"] code is as follows:

Copy Code code 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 match parameter passed in is equal to:

Copy Code code as follows:

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

Copy Code code as follows:

unquoted =!match[5] && match[2]

unquoted = ". Class:contain (' span '): EQ (3")

Copy Code code as follows:

if (matchexpr["Child"].test (match[0)) {
return null;
}

Match[0] = ": Not (. Class:contain (' span ')): eq (3)", does not match matchexpr["child" regular expression, does not perform a return NULL statement.

Copy Code code as follows:

if (Match[3] && match[4]!== undefined) {
MATCH[2] = match[4];
}

Because Match[3] and match[4] are equal to undefined, the statement body that executes else is executed.

Copy Code code as follows:

else if (unquoted
&& Rpseudo.test (unquoted)
&& (excess = Tokenize (unquoted, True))
&& (excess = Unquoted.indexof (")", unquoted.length-excess)-unquoted.length)

At this point, unquoted = ". Class:contain (' span ')): eq (3", true, and because unquoted contains: contain (' span '), matches the regular expression Rpseudo, so Rpseudo.test ( unquoted) is true, and then calls Tokenize again to parse the unquoted again, as follows:

Copy Code code as follows:

Excess = Tokenize (unquoted, True)

When the Tokenize function is called, the incoming selector argument equals ". Class:contain (' span '): EQ (3), Parseonly equals True. function body execution process is as follows:

Copy Code code as follows:

Sofar = selector;

Sofar = ". Class:contain (' span '): EQ (3")
The first time you enter the while loop, because matched has not yet been assigned, execute the following body of the statement within the IF, which initializes the tokens variable and presses the tokens into the groups array.

Copy Code code as follows:

Groups.push (tokens = []);

After that, enter the for statement.

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

Copy Code code as follows:

if (match = Matchexpr[type].exec (Sofar))
&& (!prefilters[type) | | (match = Prefilters[type]
(match))) {

Because the current remaining selector is not a tag selector, it ends this cycle.
Second for loop: Remove the second element "CLASS" from the Expr.filter to the type variable and execute the loop body code.

The results of match = Matchexpr[type].exec (Sofar) are as follows:

Match = ["Class", "class"]

Because there is no prefilters["CLASS", it executes the statement body within IF.

Copy Code code as follows:

matched = Match.shift ();

Removes the first element class in match and assigns the element to the matched variable, at which point matched= "class", match = ["Class"]

Copy Code code as follows:

Tokens.push ({
Value:matched,
Type:type,
Matches:match
}

Creates a new object {value: "Class", type: "Class", Matches: ["Class"]}, and presses the object into the tokens array.

Copy Code code as follows:

Sofar = Sofar.slice (matched.length);

Sofar variable Delete class, at this time, Sofar = ": Contain (' span ')): eq (3"
Third for loop: Remove the third element "ATTR" from the Expr.filter and assign it to the type variable, executing the loop body code.
Also, because the current remaining selector is not a property selector, it ends the loop.

Fourth time for loop: Remove the fourth element "child" from Expr.filter and assign it to the type variable, executing the loop body code.
Similarly, because the current remaining selector is not a child selector, the loop is ended.

Fifth time for loop: Remove the Fifth Element "PSEUDO" from Expr.filter and assign it to the type variable, executing the loop body code.

Copy Code code as follows:

if (match = Matchexpr[type].exec (Sofar))
&& (!prefilters[type) | | (match = Prefilters[type]
(match))) {

The results of match = Matchexpr[type].exec (Sofar) are as follows:
[": Contain (' span ')", "contain", "span", "" "," span ", undefined, undefined, undefined, undefined, undefined, undefined]

Because there is a prefilters["PSEUDO", the following code is executed:

Copy Code code as follows:

Match = Prefilters[type] (match)

The prefilters["PSEUDO"] code is not listed here, as shown above.

Copy Code code 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 match parameter passed in is equal to:
[": Contain (' span ')", "contain", "span", "" "," span ", undefined, undefined, undefined, undefined, undefined, undefined]

Copy Code code as follows:

unquoted =!match[5] && match[2];

unquoted = "span"

Copy Code code as follows:

if (matchexpr["Child"].test (match[0)) {
return null;
}

The internal statement body is not executed because ": contain (' span ')" does not match the matchexpr["child" regular expression.

Copy Code code as follows:

if (Match[3] && match[4]!== undefined) {
MATCH[2] = match[4];
}

Because match[3] = "'", match[4] = "span", so the "span" is given match[2 by executing the inner statement body of the IF)

Copy Code code as follows:

Return Match.slice (0, 3);

Returns a copy of the top three elements of match
At this point, return to the Tokenize method's for loop to continue execution, at which point the values of the variables are as follows:

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

Sofar = ": Contain (' span ')): eq (3"

Copy Code code as follows:

matched = Match.shift ();

Removes the match array with the ": Contain (' span ')" and assigns the matched variable

Copy Code code as follows:

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.

Copy Code code as follows:

Sofar = Sofar.slice (matched.length);

Sofar variable Delete ": Contain (' span ')", at this point, sofar= "): eq (3)", then exit the while loop until the for loop ends and the while loop is executed again, and there is no valid selector.

Copy Code code as follows:

Return parseonly? SoFar.length:soFar? Sizzle.error (selector):
Tokencache (selector, groups). Slice (0);

Since parseonly = True at this time, it returns the Sofar length of 6 and continues to execute the code prefilters["PSEUDO"

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

Excess = Unquoted.indexof (")", unquoted.length-excess)-unquoted.length

Calculated: Not selector End position (right parenthesis position) 22

Copy Code code as follows:

Match[0] = match[0].slice (0, excess);
MATCH[2] = unquoted.slice (0, excess);

The complete: not selector string (Match[0]) and the string within the parentheses (MATCH[2) are computed separately to equal:

Match[0] = ": Not (. Class:contain (' span '))"

MATCH[2] = ". Class:contain (' span ')"

Copy Code code as follows:

Return Match.slice (0, 3);

Returns a copy of the first three elements in match.
Return to the Tokenize function, at which time match = [": Not (. Class:contain (' span ')]", "not", ". Class:contain (' span ')"]

Copy Code code as follows:

matched = Match.shift ();

Removes the first element in match: not (. Class:contain (' span ')) and assigns the element to the matched variable, at which point matched= "": Not (. Class:contain (' span ')) ' ",
Match = ["not", ". Class:contain (' span ')]"

Copy Code code 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. At this point, there are two elements tokens are div and not selector respectively.

Copy Code code as follows:

Sofar = Sofar.slice (matched.length);

Sofar variable Delete ": Not (. Class:contain (' span ')", at this point, sofar= ": eq (3)", after ending this for loop, returns to the while loop again, in the same way, gets the third element eq selector of tokens, The process is not the same as not, and there is no more detail here. The results of the final groups are 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 Code code as follows:

Return parseonly? SoFar.length:soFar? Sizzle.error (selector):
Tokencache (selector, groups). Slice (0);

Because parseonly = undefined, execution Tokencache (selector, groups). Slice (0), which presses the groups into the cache and returns a copy of it.
This completes all the parsing, and some may ask, the second element here is not parsed, yes, this needs to be resolved again in the actual operation. Of course, if you could just parse it here. " Class:contain (' span ')): EQ (3) Saves the results of a valid selector to the cache, so you can avoid parsing again and increasing execution speed. But this only increases the current speed of this operation. Because during execution, when the ". Class:contain (' span") is resubmitted for resolution, it is saved to the cache.

At this point, the entire execution process has ended.

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.