jQuery-1.9.1 Source Analysis Series (iii) Sizzle Selector engine--Summary and performance analysis

Source: Internet
Author: User

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:

    1. Write jquery in a sequential way faster
    2. Multi-use #id selector, always narrow the selection with the #id selector
    3. 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.
    4. Try to replace the jquery custom Pseudo-class with the Advanced browser native CSS selector.
    5. Avoid overly restrictive and redundant constraints.
    6. You can avoid recursive matching by using the affinity selector > and + to avoid using and ~.
    7. Try to avoid using pseudo-classes.
    8. 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

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.