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.
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);
>
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.
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.
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é.
Supprimez le premier élément div de la correspondance et attribuez l'élément à la variable correspondante. À ce stade, matched="div", match = ["div"]
.Créez un nouvel objet { valeur : "div", tapez : "TAG", matches : ["div"] } et poussez l'objet dans le tableau de jetons.
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.
É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.
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é :
preFilters["PSEUDO"] est le suivant :
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 à :
unquoted = ".class:contain('span')):eq(3"
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 .
Puisque match[3] et match[4] sont tous deux égaux à undefined, le corps de l'instruction else est exécuté.
À 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 :
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 :
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.
, 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.
É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é.
Supprimez la première classe d'élément dans match et attribuez l'élément à la variable correspondante. À ce moment, matched="class", match = ["class"]
.Créez un nouvel objet { value : "class", tapez : "CLASS", matches : ["class"] } et poussez l'objet dans le tableau de jetons.
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.
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é :
Le code preFilters["PSEUDO"] est affiché ci-dessus et ne sera pas répertorié ici.
Comme ":contain('span')" ne correspond pas à l'expression régulière matchExpr["CHILD"], le corps de l'instruction interne n'est pas exécuté.
Puisque match[3] = "'" et match[4] ="span", le corps de l'instruction if interne est exécuté et "span" est attribué à match[2]
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"
Supprimez ":contain('span')" du tableau de correspondance et attribuez-le à la variable correspondante
Créez un nouvel objet { valeur :
":contain('span')", tapez : "PSEUDO", matches: ["contain", "span"] } et poussez l'objet dans le tableau de jetons.
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.
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é
Attribuez 6 à la variable excédentaire, puis le code
Calculer : pas la position finale du sélecteur (c'est-à-dire la position du support droit) 22
match[0] = ":not(.class:contain('span'))"
match[2] = ".class:contain('span')"
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')"]
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')"]
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.
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"] }
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.