다음 분석은 jQuery-1.10.2.js 버전을 기준으로 작성되었습니다.
다음은 $("div:not(.class:contain('span')):eq(3)")를 예로 들어 토큰화 및 preFilter 코드가 어떻게 조정되어 구문 분석을 완료하는지 설명합니다. tokenize 메소드와 preFilter 클래스의 각 코드 라인에 대한 자세한 설명을 알고 싶으시면 다음 두 글을 참고해주세요.
http://www.jb51.net/article/63155.htm
http://www.jb51.net/article/63163.htm
다음은 tokenize 메소드의 소스코드입니다. 단순화를 위해 캐싱, 쉼표 매칭, 관계형 문자 매칭과 관련된 코드를 모두 제거하고 현재 예시와 관련된 핵심 코드만 남겨두었습니다. 제거된 코드는 매우 간단합니다. 필요한 경우 위 기사를 읽어보세요.
또한 설명 텍스트 위에 코드가 적혀 있습니다.
동안(지금까지) {
if (!matched) {
groups.push(토큰 = []);
}
일치 = 거짓;
for (Expr.filter 입력) {
If ((match = matchExpr[type].exec(soFar))
&& (!preFilters[유형] || (일치 = preFilters[유형]
(일치)))) {
일치 = match.shift();
토큰.푸시({
값: 일치,
유형 : 유형,
일치: 일치
});
SoFar = soFar.slice(matched.length);
}
}
if (!matched) {
휴식;
}
}
return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) :
tokenCache(선택기, 그룹).slice(0);
}
soFar = "div:not(.class:contain('span')):eq(3)"
처음으로 while 루프에 들어갈 때 match에는 값이 할당되지 않았기 때문에 if의 다음 문 본문이 실행됩니다. 토큰 변수를 초기화하고 토큰을 그룹 배열에 푸시합니다.
첫 번째 for 루프: Expr.filter에서 첫 번째 요소 "TAG"를 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.
match = matchExpr[type].exec(soFar)의 실행 결과는 다음과 같습니다.
일치 =["div", "div"]
예제의 첫 번째 선택자는 div이며 matchExpr["TAG"]의 정규 표현식과 일치하며 preFilters["TAG"]가 존재하지 않으므로 if 내의 문 본문이 실행됩니다.
일치 항목의 첫 번째 요소인 div를 제거하고 해당 요소를 일치하는 변수에 할당합니다. 이때, match="div", match = ["div"]
새 개체 { 값: "div", 유형: "TAG", 일치: ["div"] }를 만들고 개체를 토큰 배열에 푸시합니다.
soFar 변수는 div를 삭제합니다. 이때 soFar=":not(.class:contain('span')):eq(3)"
두 번째 for 루프: Expr.filter에서 두 번째 요소 "CLASS"를 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.
현재 soFar=":not(.class:contain('span')):eq(3)"은 CLASS 유형의 정규 표현식과 일치하지 않으므로 이 루프가 종료됩니다.
세 번째 for 루프: Expr.filter에서 세 번째 요소 "ATTR"을 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.
마찬가지로 현재 나머지 선택자는 속성 선택자가 아니므로 이 주기가 종료됩니다.
네 번째 for 루프: Expr.filter에서 네 번째 요소 "CHILD"를 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.
마찬가지로 현재 남아 있는 선택자는 CHILD 선택자가 아니므로 이 주기가 종료됩니다.
다섯 번째 for 루프: Expr.filter에서 다섯 번째 요소 "PSEUDO"를 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.
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(따옴표 없음)
&& (초과 = 토큰화(따옴표 없음, 참))
&& (초과 = unquoted.indexOf(")", unquoted.length
- 초과)
- 인용되지 않은 길이)) {
match[0] = match[0].slice(0, 초과);
match[2] = 인용되지 않은.slice(0, 초과);
}
match.slice(0, 3) 반환;
}
전달된 일치 매개변수는 다음과 같습니다.
따옴표가 없음 = ".class:contain('span')):eq(3"
match[0] = ":not(.class:contain('span')):eq(3)"은 matchExpr["CHILD"] 정규 표현식과 일치하지 않으며 return null 문을 실행하지 않습니다. .
처음으로 while 루프에 들어갈 때 match에는 값이 할당되지 않았기 때문에 if의 다음 문 본문이 실행됩니다. 토큰 변수를 초기화하고 토큰을 그룹 배열에 푸시합니다.
첫 번째 for 루프: Expr.filter에서 첫 번째 요소 "TAG"를 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.
두 번째 for 루프: Expr.filter에서 두 번째 요소 "CLASS"를 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.
일치 = ["클래스", "클래스"]
preFilters["CLASS"]가 존재하지 않으므로 if 내의 문 본문이 실행됩니다.
새 개체 { 값: "클래스", 유형: "CLASS", 일치 항목: ["클래스"] }를 만들고 개체를 토큰 배열에 푸시합니다.
soFar 변수는 이때 클래스를 삭제합니다. soFar = ":contain('span')):eq(3"
세 번째 for 루프: Expr.filter에서 세 번째 요소 "ATTR"을 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.
마찬가지로 현재 나머지 선택자는 속성 선택자가 아니므로 이 주기가 종료됩니다.
네 번째 for 루프: Expr.filter에서 네 번째 요소 "CHILD"를 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.
마찬가지로 현재 남아 있는 선택자는 CHILD 선택자가 아니므로 이 주기가 종료됩니다.
다섯 번째 for 루프: Expr.filter에서 다섯 번째 요소 "PSEUDO"를 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.
match = matchExpr[type].exec(soFar)의 실행 결과는 다음과 같습니다.
[":contain('span')", "contain", "'span'", "'", "span", 정의되지 않음, 정의되지 않음, 정의되지 않음, 정의되지 않음, 정의되지 않음, 정의되지 않음]
preFilters["PSEUDO"]가 존재하므로 다음 코드가 실행됩니다.
preFilters["PSEUDO"] 코드는 위에 표시되어 있으며 여기에 나열되지 않습니다.
코드 복사
":contain('span')"은 matchExpr["CHILD"] 정규식과 일치하지 않으므로 내부 문 본문이 실행되지 않습니다.
match[3] = "'"이고 match[4] ="span"이므로 내부 if 문 본문이 실행되고 "span"이 match[2]에 할당됩니다.
일치의 처음 세 요소의 복사본을 반환합니다
이때, tokenize 메소드의 for 루프로 돌아가서 실행을 계속합니다. 이때, 각 변수의 값은 다음과 같습니다.
일치 = [":contain('span')", "contain", "span"]
soFar = ":contain('span')):eq(3"
일치 배열에서 ":contain('span')"을 제거하고 일치하는 변수에 할당합니다.
새 개체 만들기 {값:
":contain('span')", 유형:"PSEUDO", ["contain", "span"] }과 일치하고 객체를 토큰 배열에 푸시합니다.
soFar 변수는 ":contain('span')"을 삭제합니다. 이때 soFar="):eq(3)"는 이후 for 루프가 끝나고 while 루프가 다시 실행될 때까지 존재합니다. 유효한 선택자가 없습니다. 따라서 while 루프를 종료하십시오.
이때는 parseOnly = true이므로 이때 soFar의 길이인 6이 반환되고, preFilters["PSEUDO"]의 코드가 계속 실행됩니다
초과 변수에 6을 할당한 후 코드
계산: 선택기 끝 위치가 아님(예: 오른쪽 대괄호 위치) 22
전체 :not 선택기 문자열(match[0])과 해당 괄호 안의 문자열(match[2])을 각각 계산합니다.
match[0] = ":not(.class:contain('span'))"
match[2] = ".class:contain('span')"
일치하는 처음 세 요소의 복사본을 반환합니다.
토큰화 함수로 돌아가서 이제 일치 = [":not(.class:contain('span'))", "not", ".class:contain('span')"]
일치에서 첫 번째 요소인 ":not(.class:contain('span'))"을 제거하고 해당 요소를 일치된 변수에 할당합니다. 이때 match="":not(.class:contain( ' 스팬'))"",
match = ["not", ".class:contain('span')"]
새 개체 만들기 { 값: ":not(.class:contain('span'))"", 유형: "PSEUDO", 일치: ["not", ".class:contain('span') "] }를 입력하고 개체를 토큰 배열에 푸시합니다. 이때 토큰에는 선택기가 아닌 div라는 두 가지 요소가 있습니다.
SoFar 변수는 ":not(.class:contain('span'))"을 삭제합니다. 이때 soFar=":eq(3)"는 이 for 루프를 종료한 후 다시 while 루프로 돌아갑니다. 같은 방식으로 토큰의 세 번째 요소에 대한 eq 선택기를 얻으려면 프로세스가 not과 일치하므로 여기서는 자세히 설명하지 않겠습니다. 최종 조별 결과는 다음과 같습니다.
group[0][0] = {값: "div", 유형: "TAG", 일치: ["div"] }
group[0][1] = {값: ":not(.class:contain('span'))", 유형: "PSEUDO", 일치: ["not", ".class:contain(' 스팬')"] }
group[0][2] = {값: ":eq(3)", 유형: "PSEUDO", 일치: ["eq", "3"] }
parseOnly = 정의되지 않았으므로 tokenCache(selector, groups).slice(0)이 실행됩니다. 이 문은 그룹을 캐시에 푸시하고 해당 복사본을 반환합니다.
여기에서 모든 구문 분석이 완료됩니다. 여기서 두 번째 요소는 구문 분석되지 않습니다. 실제 작업에서 다시 구문 분석해야 합니다. 물론 방금 "class:contain('span')):eq(3"을 구문 분석할 때 유효한 선택기의 결과를 캐시에 저장할 수 있다면 다시 구문 분석하는 것을 방지하고 실행 속도를 향상시킬 수 있습니다. 실행 중에 ".class:contain('span')"이 분석을 위해 다시 제출되면 캐시에 저장되므로 현재 실행 속도만 향상됩니다.