>  기사  >  웹 프론트엔드  >  jQuery 선택기 소스 코드 해석(5): tokenize_jquery의 구문 분석 프로세스

jQuery 선택기 소스 코드 해석(5): tokenize_jquery의 구문 분석 프로세스

WBOY
WBOY원래의
2016-05-16 16:06:451215검색

다음 분석은 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 메소드의 소스코드입니다. 단순화를 위해 캐싱, 쉼표 매칭, 관계형 문자 매칭과 관련된 코드를 모두 제거하고 현재 예시와 관련된 핵심 코드만 남겨두었습니다. 제거된 코드는 매우 간단합니다. 필요한 경우 위 기사를 읽어보세요.

또한 설명 텍스트 위에 코드가 적혀 있습니다.

코드 복사 코드는 다음과 같습니다.

함수 토큰화(선택기, 구문 분석 전용) {
var 일치, 일치, 토큰, 유형, soFar, 그룹, preFilters;

soFar = 선택기;
그룹 = [];
preFilters = Expr.preFilter;

동안(지금까지) {
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);
}


먼저, jQuery 실행 중 select 메소드에 의해 tokenize가 처음으로 호출되고, "div:not(.class:contain('span')):eq(3)"이 선택기 매개변수로 메소드에 전달됩니다.
코드 복사 코드는 다음과 같습니다.

soFar = 선택기;

soFar = "div:not(.class:contain('span')):eq(3)"
처음으로 while 루프에 들어갈 때 match에는 값이 할당되지 않았기 때문에 if의 다음 문 본문이 실행됩니다. 토큰 변수를 초기화하고 토큰을 그룹 배열에 푸시합니다.

코드 복사 코드는 다음과 같습니다.

groups.push(토큰 = [])

그런 다음 for 문을 입력합니다.

첫 번째 for 루프: Expr.filter에서 첫 번째 요소 "TAG"를 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.

코드 복사 코드는 다음과 같습니다.

If ((match = matchExpr[type].exec(soFar))
&& (!preFilters[유형] || (일치 = preFilters[유형]
(일치)))) {

match = matchExpr[type].exec(soFar)의 실행 결과는 다음과 같습니다.

일치 =["div", "div"]

예제의 첫 번째 선택자는 div이며 matchExpr["TAG"]의 정규 표현식과 일치하며 preFilters["TAG"]가 존재하지 않으므로 if 내의 문 본문이 실행됩니다.

코드 복사 코드는 다음과 같습니다.

일치 = match.shift()

일치 항목의 첫 번째 요소인 div를 제거하고 해당 요소를 일치하는 변수에 할당합니다. 이때, match="div", match = ["div"]

코드 복사 코드는 다음과 같습니다.

토큰.푸시({
값: 일치,
유형 : 유형,
       일치: 일치
}

새 개체 { 값: "div", 유형: "TAG", 일치: ["div"] }를 만들고 개체를 토큰 배열에 푸시합니다.

코드 복사 코드는 다음과 같습니다.

SoFar = soFar.slice(matched.length);

soFar 변수는 div를 삭제합니다. 이때 soFar=":not(.class:contain('span')):eq(3)"
두 번째 for 루프: Expr.filter에서 두 번째 요소 "CLASS"를 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.

코드 복사 코드는 다음과 같습니다.

If ((match = matchExpr[type].exec(soFar))
&& (!preFilters[유형] || (일치 = preFilters[유형]
(일치)))) {

현재 soFar=":not(.class:contain('span')):eq(3)"은 CLASS 유형의 정규 표현식과 일치하지 않으므로 이 루프가 종료됩니다.
세 번째 for 루프: Expr.filter에서 세 번째 요소 "ATTR"을 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.
마찬가지로 현재 나머지 선택자는 속성 선택자가 아니므로 이 주기가 종료됩니다.

네 번째 for 루프: Expr.filter에서 네 번째 요소 "CHILD"를 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.
마찬가지로 현재 남아 있는 선택자는 CHILD 선택자가 아니므로 이 주기가 종료됩니다.

다섯 번째 for 루프: Expr.filter에서 다섯 번째 요소 "PSEUDO"를 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.

코드 복사 코드는 다음과 같습니다.

If ((match = matchExpr[type].exec(soFar))
&& (!preFilters[유형] || (일치 = preFilters[유형]
(일치)))) {

match = matchExpr[type].exec(soFar)의 실행 결과는 다음과 같습니다.
[":not(.class:contain('span')):eq(3)", "not", ".class:contain('span')):eq(3", 정의되지 않음, 정의되지 않음, 정의되지 않음, 정의되지 않음 , 정의되지 않음, 정의되지 않음, 정의되지 않음, 정의되지 않음]

preFilters["PSEUDO"]가 존재하므로 다음 코드가 실행됩니다.

코드 복사 코드는 다음과 같습니다.

일치 = preFilters[유형](일치)

preFilters["PSEUDO"] 코드는 다음과 같습니다.

코드 복사 코드는 다음과 같습니다.

"PEUDO": 함수(일치) {
var 초과, 인용되지 않음 = !match[5] && match[2];

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) 반환;
}

전달된 일치 매개변수는 다음과 같습니다.

코드 복사 코드는 다음과 같습니다.

[":not(.class:contain('span')):eq(3)", "not", ".class:contain('span')):eq(3", 정의되지 않음, 정의되지 않음, 정의되지 않음, 정의되지 않음 , 정의되지 않음

코드 복사 코드는 다음과 같습니다.

따옴표 없음 = !match[5] && match[2]

따옴표가 없음 = ".class:contain('span')):eq(3"

코드 복사 코드는 다음과 같습니다.

if (matchExpr["CHILD"].test(match[0])) {
null 반환
}

match[0] = ":not(.class:contain('span')):eq(3)"은 matchExpr["CHILD"] 정규 표현식과 일치하지 않으며 return null 문을 실행하지 않습니다. .

코드 복사 코드는 다음과 같습니다.

if (일치[3] && match[4] !== 정의되지 않음) {
일치[2] = 일치[4]; }

match[3]과 match[4]는 모두 undefine이므로 else 문 본문이 실행됩니다.

코드 복사 코드는 다음과 같습니다.
else if(인용되지 않음
              && rpseudo.test(따옴표 없음)  
​​​​&& (초과 = 토큰화(따옴표 없음, true))
​​​​&& (초과 = unquoted.indexOf(")", unquoted.length - 초과) - unquoted.length)

이때 unquoted = ".class:contain('span')):eq(3"은 true이고, unquoted가 contain:contain('span')이므로 정규식 rpseudo와 일치하므로 rpseudo입니다. test(unquoted)가 true이고, 다음과 같이 tokenize를 다시 호출하여 인용되지 않은 부분을 다시 구문 분석합니다.


코드 복사 코드는 다음과 같습니다.
초과 = 토큰화(따옴표 없음, true)

이번에 토큰화 함수를 호출할 때 들어오는 선택기 매개변수는 ".class:contain('span')):eq(3"과 같고,parseOnly는 true와 같습니다. 함수 본문의 실행 프로세스는 다음과 같습니다. 다음과 같습니다:

코드 복사 코드는 다음과 같습니다.
지금까지 = 선택기

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

처음으로 while 루프에 들어갈 때 match에는 값이 할당되지 않았기 때문에 if의 다음 문 본문이 실행됩니다. 토큰 변수를 초기화하고 토큰을 그룹 배열에 푸시합니다.

코드 복사 코드는 다음과 같습니다.
groups.push(토큰 = [])

뒤에 for 문을 입력합니다.

첫 번째 for 루프: Expr.filter에서 첫 번째 요소 "TAG"를 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.


코드 복사 코드는 다음과 같습니다.
if ((match = matchExpr[type].exec(soFar))
          && (!preFilters[유형] || (일치 = preFilters[유형]
(일치)))) {

현재 남은 선택기는 TAG 선택기가 아니므로 이번 사이클이 종료됩니다.

두 번째 for 루프: Expr.filter에서 두 번째 요소 "CLASS"를 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.

match = matchExpr[type].exec(soFar)의 실행 결과는 다음과 같습니다.

일치 = ["클래스", "클래스"]

preFilters["CLASS"]가 존재하지 않으므로 if 내의 문 본문이 실행됩니다.


코드 복사 코드는 다음과 같습니다.
일치 = match.shift()

일치 항목의 첫 번째 요소 클래스를 제거하고 해당 요소를 일치하는 변수에 할당합니다. 이때, match="class", match = ["class"]

코드 복사 코드는 다음과 같습니다.
토큰.푸시({
값 : 일치,
유형 : 유형,
일치 : 일치
}

새 개체 { 값: "클래스", 유형: "CLASS", 일치 항목: ["클래스"] }를 만들고 개체를 토큰 배열에 푸시합니다.

코드 복사 코드는 다음과 같습니다.

soFar = soFar.slice(matched.length)

soFar 변수는 이때 클래스를 삭제합니다. soFar = ":contain('span')):eq(3"
세 번째 for 루프: Expr.filter에서 세 번째 요소 "ATTR"을 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.
마찬가지로 현재 나머지 선택자는 속성 선택자가 아니므로 이 주기가 종료됩니다.

네 번째 for 루프: Expr.filter에서 네 번째 요소 "CHILD"를 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.
마찬가지로 현재 남아 있는 선택자는 CHILD 선택자가 아니므로 이 주기가 종료됩니다.

다섯 번째 for 루프: Expr.filter에서 다섯 번째 요소 "PSEUDO"를 가져와서 유형 변수에 할당하고 루프 본문 코드를 실행합니다.

코드 복사 코드는 다음과 같습니다.

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

match = matchExpr[type].exec(soFar)의 실행 결과는 다음과 같습니다.
[":contain('span')", "contain", "'span'", "'", "span", 정의되지 않음, 정의되지 않음, 정의되지 않음, 정의되지 않음, 정의되지 않음, 정의되지 않음]

preFilters["PSEUDO"]가 존재하므로 다음 코드가 실행됩니다.

코드 복사 코드는 다음과 같습니다.

일치 = preFilters[유형](일치)

preFilters["PSEUDO"] 코드는 위에 표시되어 있으며 여기에 나열되지 않습니다.

코드 복사 코드는 다음과 같습니다.

"PEUDO": 함수(일치) {
var 초과, 따옴표 없음 = !match[5] && match[2]

If (matchExpr["CHILD"].test(match[0])) {
         null 반환;                               }  

If (일치[3] && match[4] !== 정의되지 않음) {
         일치[2] = 일치[4];
} else if(인용되지 않음
>                                                                                                         && (초과 = 토큰화(인용 안함, 참))                                                                   && (초과 = unquoted.indexOf(")", unquoted.length 
~
- 인용되지 않은 길이)) {

         match[0] = match[0].slice(0, 초과)
         match[2] = unquoted.slice(0, 초과)
}  

match.slice(0, 3) 반환
}



들어오는 일치 매개변수는 다음과 같습니다.
[":contain('span')", "contain", "'span'", "'", "span", 정의되지 않음, 정의되지 않음, 정의되지 않음, 정의되지 않음, 정의되지 않음, 정의되지 않음]


코드 복사 코드는 다음과 같습니다. 따옴표 없음 = !match[5] && match[2];

따옴표 없음 = "범위"


코드 복사

코드는 다음과 같습니다. if (matchExpr["CHILD"].test(match[0])) { null을 반환합니다.
}

":contain('span')"은 matchExpr["CHILD"] 정규식과 일치하지 않으므로 내부 문 본문이 실행되지 않습니다.

코드 복사 코드는 다음과 같습니다.

if (match[3] && match[4] !== 정의되지 않음) {
일치[2] = 일치[4];
}

match[3] = "'"이고 match[4] ="span"이므로 내부 if 문 본문이 실행되고 "span"이 match[2]에 할당됩니다.

코드 복사 코드는 다음과 같습니다.

return match.slice(0, 3)

일치의 처음 세 요소의 복사본을 반환합니다
이때, tokenize 메소드의 for 루프로 돌아가서 실행을 계속합니다. 이때, 각 변수의 값은 다음과 같습니다.

일치 = [":contain('span')", "contain", "span"]

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

코드 복사 코드는 다음과 같습니다.

일치 = match.shift()

일치 배열에서 ":contain('span')"을 제거하고 일치하는 변수에 할당합니다.

코드 복사 코드는 다음과 같습니다.

토큰.푸시({
값 : 일치,
유형 : 유형,
일치 : 일치
}


새 개체 만들기 {값:
":contain('span')", 유형:"PSEUDO", ["contain", "span"] }과 일치하고 객체를 토큰 배열에 푸시합니다.

코드 복사 코드는 다음과 같습니다.

soFar = soFar.slice(matched.length)

soFar 변수는 ":contain('span')"을 삭제합니다. 이때 soFar="):eq(3)"는 이후 for 루프가 끝나고 while 루프가 다시 실행될 때까지 존재합니다. 유효한 선택자가 없습니다. 따라서 while 루프를 종료하십시오.

코드 복사 코드는 다음과 같습니다.

반환 parsOnly ? soFar.length : soFar ? Sizzle.error(selector) :
tokenCache(선택기, 그룹).slice(0)

이때는 parseOnly = true이므로 이때 soFar의 길이인 6이 반환되고, preFilters["PSEUDO"]의 코드가 계속 실행됩니다

코드 복사 코드는 다음과 같습니다.

else if(인용되지 않음
              && rpseudo.test(따옴표 없음)  
​​​​&& (초과 = 토큰화(따옴표 없음, true))
​​​​&& (초과 = unquoted.indexOf(")", unquoted.length - 초과) - unquoted.length)

초과 변수에 6을 할당한 후 코드

코드 복사 코드는 다음과 같습니다.

초과 = unquoted.indexOf(")", unquoted.length - 초과) - unquoted.length

계산: 선택기 끝 위치가 아님(예: 오른쪽 대괄호 위치) 22

코드 복사 코드는 다음과 같습니다.

match[0] = match[0].slice(0, 초과)
match[2] = unquoted.slice(0, 초과)

전체 :not 선택기 문자열(match[0])과 해당 괄호 안의 문자열(match[2])을 각각 계산합니다.

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

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

코드 복사 코드는 다음과 같습니다.

return match.slice(0, 3)

일치하는 처음 세 요소의 복사본을 반환합니다.
토큰화 함수로 돌아가서 이제 일치 = [":not(.class:contain('span'))", "not", ".class:contain('span')"]

코드 복사 코드는 다음과 같습니다.

일치 = match.shift()

일치에서 첫 번째 요소인 ":not(.class:contain('span'))"을 제거하고 해당 요소를 일치된 변수에 할당합니다. 이때 match="":not(.class:contain( ' 스팬'))"",
match = ["not", ".class:contain('span')"]

코드 복사 코드는 다음과 같습니다.

토큰.푸시({
값 : 일치,
유형 : 유형,
일치 : 일치
}

새 개체 만들기 { 값: ":not(.class:contain('span'))"", 유형: "PSEUDO", 일치: ["not", ".class:contain('span') "] }를 입력하고 개체를 토큰 배열에 푸시합니다. 이때 토큰에는 선택기가 아닌 div라는 두 가지 요소가 있습니다.

코드 복사 코드는 다음과 같습니다.

soFar = soFar.slice(matched.length)

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"] }

코드 복사 코드는 다음과 같습니다.

반환 parsOnly ? soFar.length : soFar ? Sizzle.error(selector) :
tokenCache(선택기, 그룹).slice(0)

parseOnly = 정의되지 않았으므로 tokenCache(selector, groups).slice(0)이 실행됩니다. 이 문은 그룹을 캐시에 푸시하고 해당 복사본을 반환합니다.
여기에서 모든 구문 분석이 완료됩니다. 여기서 두 번째 요소는 구문 분석되지 않습니다. 실제 작업에서 다시 구문 분석해야 합니다. 물론 방금 "class:contain('span')):eq(3"을 구문 분석할 때 유효한 선택기의 결과를 캐시에 저장할 수 있다면 다시 구문 분석하는 것을 방지하고 실행 속도를 향상시킬 수 있습니다. 실행 중에 ".class:contain('span')"이 분석을 위해 다시 제출되면 캐시에 저장되므로 현재 실행 속도만 향상됩니다.

이 시점에서 전체 실행 과정이 종료되었습니다.

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.