The jquery version I analyzed was 1.8.3. Sizzle code starts from 3669 lines to 5358 lines, nearly 2000 lines of code, the engine version is still relatively old, the latest version has been v2.2.2, the code has more than 2000 lines. And there's a dedicated sizzle homepage.
Starting with a demo, the HTML code is as follows:
<div id= "Grand_father" > <div id= "Father" > <div id= "child1" class= "Child" > Subset 1</div> <div id= "child2" class= "Child" > Subset 2</div> <div id= "child3" class= "Child" > Subset 3</div> <input type= "Radio" id= "Radio1"/> </div></div>
Then the JavaScript code is as follows:
var $nodes = $ (' div + input[type= ' radio "],div.child:first-child '); Console.log ($nodes);
1) A jquery object is returned, as shown, and matched to two tags, a div and a radio,
2) Right div at 0 position, radio in 1 position, this indicates jquery selector match is from right to left!
The following is a flowchart that occurs when I write a $ (' div + input[type= ' radio "],div.child:first-child ') after the process:
First, the jquery object
The object needs to be new, but jquery generates an object only after $ ("xxx").
1) jquery constructor
In line 42nd, a new object will be returned:
jquery = function (selector, context) { //The jquery object is actually just the Init constructor ' enhanced ' ret Urn New JQuery.fn.init (selector, context, rootjquery);}
2) JQuery Object structure
As you can see in the graph of the returned object above:
A. The prototype property of the object __proto__ points to the prototype attribute prototype of the function jquery. __PROTO__ is internal [[Prototype]], and the prototype chain is implemented by this property.
B. The index is 0 and 1, actually the native object in the browser, we can have a simple selector to verify, for example $ ("#radio1"), the code will be executed to 140 lines
Elem = document.getElementById (match[2]);//Check parentnode to catch when Blackberry 4.6 returns//nodes that is no long Er in the document #6963if (Elem && elem.parentnode) { //Handle the case where IE and Opera return items By name instead of ID if (elem.id!== match[2]) { return Rootjquery.find (selector); } Otherwise, we inject the element directly into the JQuery object this.length = 1; This[0] = Elem;} This.context = Document;this.selector = Selector;return this;
Second, select function
The 5116-row Select function is the entry for the engine:
1) The lexical analysis function tokenize is quoted here.
2) When Tokenize returns only one of the token collection arrays, it will look for the seed collection "available through some native DOM interfaces", which can be seen in 5147 rows:
/* Complete find on line 4089, easy find as follows: Expr.find = { ' ID ': Context.getelementbyid, ' CLASS ': Context.getelementsbyclassname, ' NAME ': context.getelementsbyname, ' TAG ': Context.getelementsbytagname} */if (find = Expr.find[type]) { //Search, expanding context for leading sibling combinators if (seed = fi nd ( token.matches[0].replace (Rbackslash, ""), rsibling.test (tokens[0].type) && Context.parentnode || Context, XML)) ) { //omit logic ... }}
3) through the compile compiler function, generate a token collection array corresponding to the match, and then return the results.
Three, lexical analysis
Advanced browsers will use the Queryselectorall method to select matches directly. The low-level browser IE6 or IE7, etc., can only enter the jquery sizzle engine to match.
For debugging convenience, I changed the code of 5182 lines to "!document.queryselectorall" so that the advanced browser also entered the sizzle engine.
1) Token format
The tokenize function of line 4684 eventually returns an array of token collections, which is a string object in the following format:
String{0: ' character 1 ', 1: ' Character 2 ',....., type: ' corresponding token type ' tag,id,class,attr,child,pseudo,name,>,+, space, ~ ', Matches: ' A regular match to the structure '}
The type is obtained from the key value in the 4150-row relative object and the filter object in the 4230 row.
2) Results returned
The ' div + input[type= ' radio '],div.child:first-child ' returns the following array:
The order returned is from left to right, input first, and then Div.
3) flow of tokenize functions
There are 4 relationship symbols in:
expr.relative = { ">": {dir: "ParentNode", First:true}, " " ": {dir:" ParentNode "}, " + ": {dir:" Previou Ssibling ", first:true}, " ~ ": {dir:" PreviousSibling "}}
Combined with the HTML structure above:
1) Grand_father and child1 belong to the relationship between ancestor and offspring (space expression)
2) father and Child1 belong to the parent-child relationship, also is the ancestor and the offspring relationship (> Expression)
3) Child1 and Child2 belong to neighboring Brotherhood (+ expression)
4) Child1 and child2,child3 are common fraternal relationships (~ expression)
Iv. compiler functions
The process of translating advanced rules into the underlying implementation is called compiling, such as high-level language to machine language. The same process of turning the abstract CSS selection syntax into a specific matching function is also a compilation.
1) Matcherfromtokens
The compile function of the 5080 row gets the matching token collection corresponding to the matcherfromtokens function referencing the 4931 rows, and the reference code is as follows:
1 i = group.length;//from right to left 2 while (i--) {3 cached = Matcherfromtokens (Group[i]); 4 if (Cached[expando]) {5 Setmatchers.push (cached); 6 } else {7 elementmatchers.push (cached); 8 }9}
Returns an array of two functions, corresponding to the above token collection array, since it is from right to left, so with the above token collection array in turn. "On line 4979 Console.log (matchers)"
Open the first value, you will find that there are also nested inside a lot of closures, closures and closures, which is the previous large matching function:
Matcherfromtokens will finally refer to the last 4803 rows of Elementmatcher, passing the above array as parameters.
The 7th line of the example code above is inserting the function into the elementmatchers array. Then pass to the following matcherfromgroupmatchers function.
2) Matcherfromgroupmatchers
The matcherfromgroupmatchers function, which references 4983 rows, generates the final match and returns the matching result.
This function will return a curry function, which is the supermatcher function of 4986 rows.
In 4995 rows, the Supermatcher function determines a starting query range based on the parameters seed, Expandcontext, and context:
Elems = Seed | | Byelement && expr.find["TAG" ("*", Expandcontext && Context.parentnode | | context)
It may be obtained directly from the seed seed collection, or within the context or context of the parent node.
Here the context is "document", that is, the entire DOM tree "in 5003 rows Console.log (Elems)", the ELEMS structure is as follows:
You can see that if you define content in advance, you'll narrow it down a lot, which is good for matching, like jquery can write:
$ (' div + input[type= ' Radio "],div.child:first-child ', $ (' #grand_father '))
Start filtering on line 5007, and the Elementmatchers parameter is the large match returned above.
The reason used for is because the selector (div + input[type= "Radio"],div.child:first-child) has the "," number, so there are two large sets of matches.
for (;(elem = elems[i])! = NULL; i++) { //omit logic ... for (j = 0; (Matcher = elementmatchers[j]); J + +) { if (Matcher (elem, Context, XML)) { results.push (elem); break; } } Omit logic ...}
Analysis of Sizzle engine in jquery