次の分析は、jQuery-1.10.2.js バージョンに基づいています。
以下では $("div:not(.class:contain('span')):eq(3)") を例として、解析を完了するためにトークン化コードと preFilter コードがどのように調整されるかを説明します。 tokenize メソッドと preFilter クラスのコードの各行の詳細な説明を知りたい場合は、次の 2 つの記事を参照してください。
http://www.jb51.net/article/63155.htm
http://www.jb51.net/article/63163.htm
以下は tokenize メソッドのソース コードです。わかりやすくするために、キャッシュ、カンマ マッチング、リレーショナル文字マッチングに関連するコードをすべて削除し、現在の例に関連するコア コードのみを残しています。削除されたコードは非常に単純です。必要に応じて、上記の記事を参照してください。
また、コードは説明文の上に書かれています。
その間 (これまで) {
if (!matched) {
groups.push(トークン = []);
}
一致 = false;
for (Expr.filter に入力) {
If ((match = matchExpr[type].exec(soFar))
&& (!preFilters[タイプ] || (match = preFilters[タイプ]
(一致)))) {
一致 = match.shift();
tokens.push({
値: 一致、
タイプ: タイプ、
一致: match
});
SoFar = soFar.slice(matched.length);
}
}
if (!matched) {
休憩;
}
}
return parseOnly ? soFar.length : soFar.error(selector) :
tokenCache(セレクター、グループ).slice(0);
}
これまで = "div:not(.class:contain('span')):eq(3)"
初めて while ループに入ると、matched には値が割り当てられていないため、if 内の次のステートメント本体が実行され、トークン変数が初期化され、トークンがグループ配列にプッシュされます。
一致 =["div", "div"]
例の最初のセレクターは div で、matchExpr["TAG"] の正規表現に一致しますが、preFilters["TAG"] が存在しないため、if 内のステートメント本体が実行されます。
新しいオブジェクト { value: "div"、type: "TAG"、matches: ["div"] } を作成し、そのオブジェクトをトークン配列にプッシュします。
soFar 変数は div を削除します。このとき、soFar=":not(.class:contain('span')):eq(3)"
2 番目の for ループ: Expr.filter から 2 番目の要素「CLASS」を取得し、それを type 変数に割り当て、ループ本体のコードを実行します。
現在の soFar=":not(.class:contain('span')):eq(3)" は CLASS 型の正規表現と一致しないため、このループは終了します。
3 番目の for ループ: Expr.filter から 3 番目の要素「ATTR」を取得し、それを type 変数に割り当て、ループ本体のコードを実行します。
同様に、現在残っているセレクターは属性セレクターではないため、このサイクルは終了します。
4 番目の for ループ: Expr.filter から 4 番目の要素「CHILD」を取得し、それを type 変数に割り当て、ループ本体のコードを実行します。
同様に、現在残っているセレクターは CHILD セレクターではないため、このサイクルは終了します。
5 番目の for ループ: Expr.filter から 5 番目の要素「PSEUDO」を取得し、それを type 変数に割り当て、ループ本体のコードを実行します。
match = matchExpr[type].exec(soFar) の実行結果は次のとおりです。
[":not(.class:contain('span')):eq(3)", "not", ".class:contain('span')):eq(3", 未定義, 未定義, 未定義, 未定義、未定義、未定義、未定義、未定義]
preFilters["PSEUDO"] が存在するため、次のコードが実行されます:
preFilters["PSEUDO"] コードは次のとおりです:
if (matchExpr["CHILD"].test(match[0])) {
null を返す;
}
if (match[3] && match[4] !== 未定義) {
一致[2] = 一致[4];
} else if (引用符なし
&& rpseudo.test(引用符なし)
&& (excess = tokenize(unquoted, true))
&& (過剰 = unquoted.indexOf(")", unquoted.length
- 過剰)
- unquoted.length)) {
match[0] = match[0].slice(0, 過剰);
match[2] = unquoted.slice(0, 過剰);
}
return match.slice(0, 3);
}
渡された一致パラメータは次と同じです:
引用なし = ".class:contain('span')):eq(3"
match = matchExpr[type].exec(soFar) の実行結果は次のとおりです。
preFilters["CLASS"]が存在しないため、if内の文本体が実行されます。
コードをコピーします
新しいオブジェクト { value: "class"、type: "CLASS"、matches: ["class"] } を作成し、そのオブジェクトをトークン配列にプッシュします。
3 番目の for ループ: Expr.filter から 3 番目の要素「ATTR」を取得し、それを type 変数に割り当て、ループ本体のコードを実行します。
同様に、現在残っているセレクターは属性セレクターではないため、このサイクルは終了します。
同様に、現在残っているセレクターは CHILD セレクターではないため、このサイクルは終了します。
[":contain('span')", "contain", "'span'", "'", "span", 未定義, 未定義, 未定義, 未定義, 未定義, 未定義]
コードをコピーします
コードは次のとおりです:
「:contain('span')」は matchExpr["CHILD"] 正規表現と一致しないため、内部ステートメント本体は実行されません。
match[3] = "'" かつ match[4] ="span" なので、内部の if 文本体が実行され、match[2] に "span" が代入されます
このとき、tokenizeメソッドのforループに戻って実行を継続します。このときの各変数の値は以下の通りです。
一致 = [":contain('span')", "contain", "span"]
これまで = ":contain('span')):eq(3"
コードをコピー
コードをコピー
コードをコピー
コードをコピー
コードは次のとおりです:
一致する最初の 3 つの要素のコピーを返します。
トークン化関数に戻り、match = [":not(.class:contain('span'))", "not", ".class:contain('span')"]
match = ["not", ".class:contain('span')"]
group[0][1] = {値: ":not(.class:contain('span'))"、タイプ: "PSEUDO"、一致: ["not"、".class:contain('スパン')"] }
グループ[0][2] = {値: ":eq(3)"、タイプ: "PSEUDO"、一致: ["eq", "3"] }