The subject part of the sizzle engine has been analyzed, and today is a full stop for this part.
A. Summary of sizzle analytic process
It's time to make a summary. The process of sizzle parsing is already at a glance.
1. The selector enters the sizzle (selector, context, results, seed) function, which does not conform to the selector (for example, there is no selector, the selector is not a string, Contextual context is not a node element and is not document) excluded, directly returned, and then judged if the selector is a simple selector (only a meta selector, such as "#ID", ". CLASS "," TAG ", and so on) directly with the browser-supported function to get the results back. Then determine if the browser supports advanced search Queryselectorall and the selector also conforms to the parameter criteria to get the results back. Otherwise enter the Select (selector, context, results, seed) function to proceed.
2. After entering the Select (Selector, context, results, seed) function. Select First parses the selector selector (tokenize (selector, parseonly) function) without an alternative seed seed and the selector is not multiple-side selector ("Div >p,input" Narrow the selection, such as a comma separated by a side-by-side selector, including the first meta-selector (the smallest selector) for the atomic Selector Group (tokens) token = Tokens[0] is "#ID" and the next token of the ID is the Relationship node (">"/"+"/""/" ~ ") to narrow the range (context = expr.find[" id "] (...) by expr.find[" id "). ); The first token of the remaining tokens is not any one of (">"/"+"/"~") and there is no Sizzle custom Position pseudo- class in the tokens (the position pseudo-class has the following: primary, last, even, Odd, EQ (n), GT (N), lt (n), find the last meta selector and ensure that there are no relationship selectors behind the meta selector, and if found, find the alternative seed based on the meta selector (
Seed = Find (Token.matches[0].replace (RuneScape, Funescape), Rsibling.test (Tokens[0].type) && Context.parentnode | | Context
), if seed is empty or if there is no selector in the tokens, the result is returned directly, otherwise the seed with the reduced extent continues to look down. Go to compile (selector, group/* Internal use only */) to continue in the compiled function.
3. Compile (selector, group/* Internal use only */) function will eventually return a curry function that performs the ultimate matching function. The curry function is eventually executed in the Select function.
Compile (selector, match) (seed, context, documentisxml, results, rsibling.test (selector))
; The compile function actually does two things. One is to use tokens to generate an ultimate matching function (cached = Matcherfromtokens (Group[i])) to press into Setmatchers or elementmatchers. The other thing is to generate and return the Curry function that performs the ultimate matching function (
cached = Compilercache (selector, matcherfromgroupmatchers (Elementmatchers, setmatchers));
)
4. Use Matcherfromtokens to generate the tokens corresponding final matching function. The matchers array is used to hold all matching function arrays. Matchers has a default matching function when initializing. Traverse the selector from left to right (0-n traversal tokens) If you encounter relationship selector (Matcher = expr.relative[Tokens[i].type]), The integrated relationship selector and the original matchers generate a new matching function (matchers = [Addcombinator (Elementmatcher (matchers), Matcher)];); If you encounter non-relational selector , gets the selector matching function (Matcher = expr.filter[Tokens[i].type].apply (null, tokens[i].matches);), in two cases, one of which is The pseudo-class selector divides the selector tokens into four parts, generates corresponding matching functions and then integrates directly back to the ultimate matching function (
return Setmatcher (i > 1 & & Elementmatcher (matchers), I > 1 && toselector (tokens.slice (0, I -1). replace (RTrim, "$" < J && Matcherfromtokens (to Kens.slice (i, J)), // J < Len && Matcherfromtokens ( Tokens = Tokens.slice (j)), // If the Pseudo-class (or Pseudo-class side-by-side selector ) there are selectors to filter
J < Len && Toselector ( Tokens)
);
), and the other non-pseudo class selector presses the meta-selector matching function into the matchers (Matchers.push (Matcher);). The final match function is then returned via Elementmatcher (matchers).
5. Use Matcherfromgroupmatchers (Elementmatchers, setmatchers) to return a curry function that performs the ultimate matching function. The internal flow of this curry function is to determine the initial lookup range, or seed, or the entire tree node. Traverse the ultimate match set elementmatchers, and place the node in the result set if the element matches
while ((Matcher = elementmatchers[j++]) { if (Matcher (elem, Context, XML)) {Results.push (elem ); Break ; }}
。 Then traverse the final matching function with pseudo class selector to do the corresponding processing
while ((Matcher = setmatchers[j++]) {matcher (unmatched, setmatched, context, XML);}
。 Returns the matching results after all processes have been completed.
This is the end of the basic process. There may be some small details inside, but the process is OK. Hope everyone has a harvest, haha.
B. Selector efficiency issues
In general, browser-native function efficiency getElementById > Getelementsbyname >= getelementsbyclassname.
Test case (borrow Arron's test case)
<div id= "Demo" class= ' demo ' > <ul> <li><input type= "checkbox" data= "Data"/></li> <l i></li > <li> </li> <li></li > </ul></div >
Test method ( timing 10 seconds to see the number of executions )
<script type= "Text/javascript" >varDatestart,dateend, Count= 0;
This is one of the casesfunctionTestID () {DateStart=NewDate (); Count= 0; while(1) {dateend=NewDate (); if(Dateend-datestart > 10000) { Console.log ( ' #demo li:nth-child (1) Run count = ' + count '); Break; } $(' #demo li:nth-child (1) '); Count++; }}
TestID ();
</script>
First set of tests: continuous writing, segmented writing, using Find method comparison
Test the number of three jquery notation executions in 10 seconds:
$ (' #demo li:nth-child (1) '): #demo li:nth-child (1) Run count = 1186316
$ (' Li:nth-child (1) ', ' #demo '): Li:nth-child (1), #demo run count = 537820
$ (' #demo '). Find (' Li:nth-child (1) '): #demo find Li:nth-child (1) Run count = 562195
Dizzy, how and Arron test results are different:Arron Test Results Click here to view
But I guess according to the source of jquery:
In two cases:
The first, support Queryselectorall under
$ (' #demo li:nth-child (1) ') mode sizzle will first use Queryselectorall query ' #demo li:nth-child (1) '
$ (' Li:nth-child (1) ', ' #demo ') means first $ (' #demo ') to get results as the context enters sizzle using Queryselectorall query ' Li:nth-child (1) '. One more step than the first.
$ ('. Demo '). Find (' Li:nth-child (1) ') similar to $ (' Li:nth-child (1) ', ' #demo '), the efficiency should be the same.
The second kind, does not support the case of Queryselectorall (I use IE7 test)
LOG: #demo li:nth-child (1) Run count = 51781
LOG: Li:nth-child (1), #demo run count = 51982
LOG: (#demo). Find (Li:nth-child (1)) Run count = 52831
There is not a big gap between them. The analysis of the sizzle process shows that three methods are first calculated "#demo" and then matched "li:nth-child (1)".
So according to the analysis, in the modern browser (Ie8+,chrome,fireforx) in these three ways ligatures is the fastest .
Test a set of data again
$ ('. Demo li:nth-child (1) '):. Demo Li:nth-child (1) Run count = 1201026
$ (' Li:nth-child (1) ', '. Demo '): Li:nth-child (1),. Demo Run count = 409030
$ ('. Demo '). Find (' Li:nth-child (1) '):. Demo find Li:nth-child (1) Run count = 417080
The result is still the fastest ligatures.
I'll switch to another way to test the above example (the number of cycles is a certain amount of time to compare ):
One of the test cases
function TestID () { new Date () = 0; while (1) { if(Count > 1000000) { break; } $ ('. Demo li:nth-child (1) '); Count++ ; } New Date (); Console.log ('. Demo li:nth-child (1) Run time = ' + (dateend- DateStart));}
Look at the results:
#demo Li:nth-child (1) Run time = 11519
Li:nth-child (1), #demo run time = 23556
(#demo). Find (Li:nth-child (1)) Run time = 22016
. Demo Li:nth-child (1) Run time = 8076
Li:nth-child (1),. Demo Run time = 20218
(. Demo). Find (Li:nth-child (1)) Run time = 20045
Or is ligatures spending the least time
also proves that my first test method ( 10 seconds to compare the number of executions ) test cases are no problem! thought I was mistaken, as for the Arron test results and I do not know why.
So according to the analysis, in the modern browser (Ie8+,chrome,fireforx) in these three ways ligatures is the fastest. The reason is to maintain the advantages of parsing from right to left .
Second group: CSS property selector, CSS pseudo-class selector comparison
$ ('. Demo li > input[type= "checkbox"]: Run count = 424106
$ ('. Demo li > Input:checkbox '): Run count = 266172
The conclusion is that the CSS property selector and CSS pseudo-class selectors are at the same magnitude, but the CSS property selector will be slightly faster , but there is no faster than the pseudo-class one in modern browsers how to use Advanced Search Queryselectorall () To query the case of course is native relatively fast. jquery's custom pseudo-classes are slower.
Group Three: CSS constraints
$ ('. Demo > UL li:nth-child (1) input[type= "checkbox"]: Run count = 355359
$ ('. Demo input[type= ' checkbox '] '): Run count = 403864
Avoid over-restraint
Group fourth: CSS filtering pseudo-classes, avoiding filtering pseudo-classes
$ ('. Demo li > Input:eq (0) ') Run count = 36565
$ ('. Demo li > Input:first ') Run count = 36224
$ ('. Demo li > Input '). EQ (0) Run count = 359582
It can be seen that pseudo-class is a major culprit in slowing down selector efficiency.
Summarize:
- Write jquery in a sequential way faster
- Multi-use #id selector, always narrow the selection with the #id selector
- The selector with the rightmost selector is characteristic, such as "#id input" can be replaced by "#id input[type= ' text ']". Because the comparison is started from right to left, the selector on the right side determines that the only result is better.
- Try to replace the jquery custom Pseudo-class with the Advanced browser native CSS selector.
- Avoid overly restrictive and redundant constraints.
- You can avoid recursive matching by using the affinity selector > and + to avoid using and ~.
- Try to avoid using pseudo-classes.
- Cache the jquery object, and then use Find on the cached object.
If you feel that this article has a little effect, please top it.
jQuery-1.9.1 Source Analysis Series (iii) Sizzle Selector engine--Summary and performance analysis