Maison  >  Article  >  interface Web  >  Interprétation du code source du sélecteur jQuery (5) : processus d'analyse de tokenize_jquery

Interprétation du code source du sélecteur jQuery (5) : processus d'analyse de tokenize_jquery

WBOY
WBOYoriginal
2016-05-16 16:06:451215parcourir

L'analyse suivante est basée sur la version jQuery-1.10.2.js.

Ce qui suit prendra $("div:not(.class:contain('span')):eq(3)") comme exemple pour expliquer comment les codes tokenize et preFilter sont coordonnés pour terminer l'analyse. Si vous souhaitez connaître l'explication détaillée de chaque ligne de code de la méthode tokenize et de la classe preFilter, merci de vous référer aux deux articles suivants :

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

Ce qui suit est le code source de la méthode tokenize. Pour plus de simplicité, j'ai supprimé tous les codes liés à la mise en cache, à la correspondance des virgules et à la correspondance des caractères relationnels, ne laissant que le code principal lié à l'exemple actuel. Le code qui a été supprimé est très simple. Si nécessaire, vous pouvez lire l'article ci-dessus.

De plus, le code est écrit au-dessus du texte de description.

Copier le code Le code est le suivant :

function tokenize(sélecteur, parseOnly) {
var correspondant, correspondance, jetons, type, jusqu'à présent, groupes, préfiltres ;

jusqu'à présent = sélecteur ;
groupes = [];
préFilters = Expr.preFilter;

tandis que (jusqu'à présent) {
si (!matched) {
groups.push(tokens = []);
>

correspondant = faux ;

pour (tapez Expr.filter) {
Si ((match = matchExpr[type].exec(soFar))
&& (!preFilters[type] || (match = preFilters[type]
(match)))) {
Correspondant = match.shift();
jetons.push({
Valeur : assortie,
Tapez : tapez,
       correspondances : correspondance
});
SoFar = soFar.slice(matched.length);
>
>

si (!matched) {
Pause ;
>
>

return parseOnly ? soFar.length : soFar Sizzle.error(selector) :
tokenCache(sélecteur, groupes).slice(0);
>


Tout d'abord, tokenize est appelé pour la première fois par la méthode select lors de l'exécution de jQuery, et "div:not(.class:contain('span')):eq(3)" est transmis à la méthode en tant que paramètre de sélection.
Copier le code Le code est le suivant :

jusqu'à présent = sélecteur ;

soFar = "div:not(.class:contain('span')):eq(3)"
Lorsque vous entrez dans la boucle while pour la première fois, puisque matching n'a reçu aucune valeur, le corps de l'instruction suivante dans if est exécuté. Cette instruction initialisera la variable tokens et poussera les jetons dans le tableau groups.

Copier le code Le code est le suivant :

groups.push(jetons = []);

Après cela, entrez l'instruction for.

La première boucle for : prenez le premier élément "TAG" de Expr.filter et affectez-le à la variable de type, et exécutez le code du corps de la boucle.

Copier le code Le code est le suivant :

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

Le résultat de l'exécution de match = matchExpr[type].exec(soFar) est le suivant :

match =["div", "div"]

Le premier sélecteur de l'exemple est div, qui correspond à l'expression régulière de matchExpr["TAG"], et preFilters["TAG"] n'existe pas, donc le corps de l'instruction dans le if est exécuté.

Copier le code Le code est le suivant :

correspondant = match.shift();

Supprimez le premier élément div de la correspondance et attribuez l'élément à la variable correspondante. À ce stade, matched="div", match = ["div"]

.

Copier le code Le code est le suivant :

jetons.push({
Valeur : assortie,
Tapez : tapez,
       correspondances : correspondance
>

Créez un nouvel objet { valeur : "div", tapez : "TAG", matches : ["div"] } et poussez l'objet dans le tableau de jetons.

Copier le code Le code est le suivant :

SoFar = soFar.slice(matched.length);

La variable soFar supprime le div. À ce moment, soFar=":not(.class:contain('span')):eq(3)"
La deuxième boucle for : prenez le deuxième élément "CLASS" de Expr.filter et affectez-le à la variable de type, puis exécutez le code du corps de la boucle.

Copier le code Le code est le suivant :

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

Étant donné que l'actuel soFar=":not(.class:contain('span')):eq(3)" ne correspond pas à l'expression régulière de type CLASS, cette boucle se termine.
La troisième boucle for : prenez le troisième élément "ATTR" de Expr.filter et affectez-le à la variable de type, puis exécutez le code du corps de la boucle.
De même, puisque les sélecteurs restants actuels ne sont pas des sélecteurs d'attributs, ce cycle se termine.

La quatrième boucle for : prenez le quatrième élément "CHILD" de Expr.filter et affectez-le à la variable de type, puis exécutez le code du corps de la boucle.
De même, puisque le sélecteur restant actuel n'est pas un sélecteur ENFANT, ce cycle se termine.

La cinquième boucle for : prenez le cinquième élément "PSEUDO" de Expr.filter et affectez-le à la variable de type, puis exécutez le code du corps de la boucle.

Copier le code Le code est le suivant :

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

Le résultat de l'exécution de match = matchExpr[type].exec(soFar) est le suivant :
[":not(.class:contain('span')):eq(3)", "not", ".class:contain('span')):eq(3", indéfini, indéfini, indéfini, indéfini , indéfini, indéfini, indéfini, indéfini]

Puisque preFilters["PSEUDO"] existe, le code suivant est exécuté :

Copier le code Le code est le suivant :

match = preFilters[type](match)
Le code

preFilters["PSEUDO"] est le suivant :

Copier le code Le code est le suivant :

"PSEUDO" : fonction(match) {
var excès, non cité = !match[5] && match[2];

if (matchExpr["CHILD"].test(match[0])) {
renvoie null ;
>

if (match[3] && match[4] !== non défini) {
match[2] = match[4];
} sinon si (non cité
&& rpseudo.test(non cité)
&& (excès = tokenize(non cité, vrai))
&& (excès = unquoted.indexOf(")", unquoted.length
- excédent)
- non cité.longueur)) {

match[0] = match[0].slice(0, excès);
match[2] = unquoted.slice(0, surplus);
>

return match.slice(0, 3);
>

Le paramètre de correspondance transmis est égal à :

Copier le code Le code est le suivant :

[":not(.class:contain('span')):eq(3)", "not", ".class:contain('span')):eq(3", indéfini, indéfini, indéfini, indéfini , indéfini

Copier le code Le code est le suivant :

non cité = !match[5] && match[2]

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

Copier le code Le code est le suivant :

if (matchExpr["CHILD"].test(match[0])) {
Renvoie null ;
>

match[0] = ":not(.class:contain('span')):eq(3)", ne correspond pas à l'expression régulière matchExpr["CHILD"] et n'exécute pas l'instruction return null .

Copier le code Le code est le suivant :

if (match[3] && match[4] !== non défini) {
Correspondance[2] = correspondance[4];
}

Puisque match[3] et match[4] sont tous deux égaux à undefined, le corps de l'instruction else est exécuté.

Copier le code Le code est le suivant :

sinon si (non cité
              && rpseudo.test(non cité)  
​​​​&& (excès = tokenize(non cité, vrai))
​​​​&& (excess = unquoted.indexOf(")", unquoted.length - excès) - unquoted.length)

À l'heure actuelle, unquoted = ".class:contain('span')):eq(3" est vrai, et comme unquoted contain:contain('span'), il correspond à l'expression régulière rpseudo, donc rpseudo. test(unquoted) est vrai, puis appelez à nouveau tokenize pour analyser à nouveau non-quoted, comme suit :

Copier le code Le code est le suivant :

excès = tokenize(non cité, vrai)

Lors de l'appel de la fonction tokenize cette fois, le paramètre du sélecteur entrant est égal à ".class:contain('span')):eq(3", et parseOnly est égal à true. Le processus d'exécution dans le corps de la fonction est comme suit :

Copier le code Le code est le suivant :

jusqu'à présent = sélecteur ;

jusqu'à présent = ".class:contain('span')):eq(3"
Lorsque vous entrez dans la boucle while pour la première fois, puisque matching n'a reçu aucune valeur, le corps de l'instruction suivante dans if est exécuté. Cette instruction initialisera la variable tokens et poussera les jetons dans le tableau groups.

Copier le code Le code est le suivant :

groups.push(jetons = []);
Après

, entrez l'instruction for.

La première boucle for : prenez le premier élément "TAG" de Expr.filter et affectez-le à la variable de type, et exécutez le code du corps de la boucle.

Copier le code Le code est le suivant :

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

Étant donné que le sélecteur restant actuel n'est pas un sélecteur TAG, ce cycle se termine.
La deuxième boucle for : prenez le deuxième élément "CLASS" de Expr.filter et affectez-le à la variable de type, puis exécutez le code du corps de la boucle.

Le résultat de l'exécution de match = matchExpr[type].exec(soFar) est le suivant :

match = ["classe", "classe"]

Puisque preFilters["CLASS"] n'existe pas, le corps de l'instruction dans if est exécuté.

Copier le code Le code est le suivant :

correspondant = match.shift();

Supprimez la première classe d'élément dans match et attribuez l'élément à la variable correspondante. À ce moment, matched="class", match = ["class"]

.

Copier le code Le code est le suivant :

jetons.push({
valeur : correspondant,
Tapez : tapez,
matchs : match
}

Créez un nouvel objet { value : "class", tapez : "CLASS", matches : ["class"] } et poussez l'objet dans le tableau de jetons.

Copier le code Le code est le suivant :

jusqu'à présent = jusqu'à présent.slice(matched.length);

La variable soFar supprime la classe à ce moment, soFar = ":contain('span')):eq(3"
La troisième boucle for : prenez le troisième élément "ATTR" de Expr.filter et affectez-le à la variable de type, puis exécutez le code du corps de la boucle.
De même, puisque les sélecteurs restants actuels ne sont pas des sélecteurs d'attributs, ce cycle se termine.

La quatrième boucle for : prenez le quatrième élément "CHILD" de Expr.filter et affectez-le à la variable de type, puis exécutez le code du corps de la boucle.
De même, puisque le sélecteur restant actuel n'est pas un sélecteur ENFANT, ce cycle se termine.

La cinquième boucle for : prenez le cinquième élément "PSEUDO" de Expr.filter et affectez-le à la variable de type, puis exécutez le code du corps de la boucle.

Copier le code Le code est le suivant :

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

Le résultat de l'exécution de match = matchExpr[type].exec(soFar) est le suivant :
[":contain('span')", "contain", "'span'", "'", "span", indéfini, indéfini, indéfini, indéfini, indéfini, indéfini]

Puisque preFilters["PSEUDO"] existe, le code suivant est exécuté :

Copier le code Le code est le suivant :

match = preFilters[type](match)

Le code preFilters["PSEUDO"] est affiché ci-dessus et ne sera pas répertorié ici.

Copier le code Le code est le suivant :

"PSEUDO" : fonction(correspondance) {
var excès, non cité = !match[5] && match[2]

Si (matchExpr["CHILD"].test(match[0])) {
         renvoie null ;                                }  

Si (match[3] && match[4] !== non défini) {
         correspondance[2] = correspondance[4]; 
} sinon si (non cité
                                                                                                                                                                                                                  && (excès = tokenize (non cité, vrai))                                                                              && (excess = unquoted.indexOf(")", unquoted.length 
                                                                                                                                                                                                                                        -
- non cité.longueur)) {

         match[0] = match[0].slice(0, excès
);          match[2] = unquoted.slice(0, excès
); }  

Renvoie match.slice(0, 3);
}



Le paramètre de correspondance entrante est égal à :
[":contain('span')", "contain", "'span'", "'", "span", indéfini, indéfini, indéfini, indéfini, indéfini, indéfini]


Copier le code Le code est le suivant : non cité = !match[5] && match[2]


non cité = "span"


Copier le code Le code est le suivant : if (matchExpr["CHILD"].test(match[0])) {
renvoie null ;
>

Comme ":contain('span')" ne correspond pas à l'expression régulière matchExpr["CHILD"], le corps de l'instruction interne n'est pas exécuté.

Copier le code Le code est le suivant :

if (match[3] && match[4] !== non défini) {
match[2] = match[4];
>

Puisque match[3] = "'" et match[4] ="span", le corps de l'instruction if interne est exécuté et "span" est attribué à match[2]

Copier le code Le code est le suivant :

return match.slice(0, 3);

Renvoie une copie des trois premiers éléments du match
A ce moment, revenez à la boucle for de la méthode tokenize pour continuer l'exécution. A ce moment, les valeurs de chaque variable sont les suivantes :

.

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

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

Copier le code Le code est le suivant :

correspondant = match.shift();

Supprimez ":contain('span')" du tableau de correspondance et attribuez-le à la variable correspondante

Copier le code Le code est le suivant :

jetons.push({
valeur : correspondant,
Tapez : tapez,
matchs : match
}


Créez un nouvel objet { valeur :
":contain('span')", tapez : "PSEUDO", matches: ["contain", "span"] } et poussez l'objet dans le tableau de jetons.

Copier le code Le code est le suivant :

jusqu'à présent = jusqu'à présent.slice(matched.length);

La variable soFar supprime ":contain('span')". À ce moment, soFar=":eq(3)", après cela, jusqu'à ce que la boucle for se termine et que la boucle while soit à nouveau exécutée, il y a pas de sélecteur valide Alors quittez la boucle while.

Copier le code Le code est le suivant :

return parseOnly ? soFar.length : soFar Sizzle.error(sélecteur) :
tokenCache(sélecteur, groupes).slice(0);

Puisque parseOnly = true à ce moment, la longueur de soFar à ce moment est renvoyée, 6, et le code de preFilters["PSEUDO"] continue d'être exécuté

Copier le code Le code est le suivant :

sinon si (non cité
              && rpseudo.test(non cité)  
​​​​&& (excès = tokenize(non cité, vrai))
​​​​&& (excess = unquoted.indexOf(")", unquoted.length - excès) - unquoted.length)

Attribuez 6 à la variable excédentaire, puis le code

Copier le code Le code est le suivant :

surplus = unquoted.indexOf(")", unquoted.length - surplus) - unquoted.length

Calculer : pas la position finale du sélecteur (c'est-à-dire la position du support droit) 22

Copier le code Le code est le suivant :

match[0] = match[0].slice(0, excès); match[2] = unquoted.slice(0, excès);

Calculez respectivement la chaîne de sélection :not complète (match[0]) et la chaîne entre parenthèses (match[2]), qui sont égales à :

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

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


return match.slice(0, 3);

Renvoie une copie des trois premiers éléments de la correspondance.
Revenez à la fonction tokenize, maintenant match = [":not(.class:contain('span'))", "not", ".class:contain('span')"]

Copier le code Le code est le suivant :

correspondant = match.shift();

Supprimez le premier élément ":not(.class:contain('span'))" dans match et attribuez l'élément à la variable correspondante. À ce stade, matched="":not(.class:contain( '). span'))"",
match = ["not", ".class:contain('span')"]

Copier le code Le code est le suivant :

jetons.push({
valeur : correspondant,
Tapez : tapez,
matchs : match
}

Créez un nouvel objet { valeur : ":not(.class:contain('span'))"", tapez : "PSEUDO", matches : ["not", ".class:contain('span') "] }, et poussez l'objet dans le tableau des jetons. À ce stade, les jetons ont deux éléments, à savoir div et non sélecteur.

Copier le code Le code est le suivant :

jusqu'à présent = jusqu'à présent.slice(matched.length);

La variable SoFar supprime ":not(.class:contain('span'))". À ce moment, soFar=":eq(3)", après avoir terminé cette boucle for, revenez à nouveau à la boucle while, le de la même manière, pour obtenir le sélecteur d'eq du troisième élément des tokens, le processus est cohérent avec not, et je n'entrerai pas dans les détails ici. Les résultats des groupes finaux sont les suivants :
group[0][0] = {valeur : "div", type : "TAG", correspondances : ["div"] }

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

group[0][2] = {valeur : ":eq(3)", type : "PSEUDO", correspondances : ["eq", "3"] }

Copier le code Le code est le suivant :

return parseOnly ? soFar.length : soFar Sizzle.error(sélecteur) :
tokenCache(sélecteur, groupes).slice(0);

Puisque parseOnly = undefined, tokenCache(selector, groups).slice(0) est exécuté. Cette instruction pousse les groupes dans le cache et renvoie sa copie.
À partir de là, toute l'analyse est terminée. Certaines personnes peuvent demander, le deuxième élément ici n'est pas analysé. Oui, cela doit être à nouveau analysé en fonctionnement réel. Bien sûr, si vous pouvez enregistrer le résultat du sélecteur valide dans le cache lorsque vous venez d'analyser "class:contain('span')):eq(3", vous pouvez éviter d'analyser à nouveau et améliorer la vitesse d'exécution. Mais cela n'améliore que la vitesse d'exécution actuelle car lors de l'exécution, lorsque ".class:contain('span')" est à nouveau soumis pour analyse, il sera stocké dans le cache.

À ce stade, tout le processus d'exécution est terminé.

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn