`
wtnhwbb
  • 浏览: 164199 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

jQuery源码学习笔记五 六 七 八 转

    博客分类:
  • JS
阅读更多
<p>在正式深入jQuery的核心功能选择器之前,还有一些方法,基本都是数组方法,用于遴选更具体的需求,如获得某个元素的所有祖选元素啦,等等。接着是其缓存机制data。</p>

<pre class="brush:javascript;gutter:false;toolbar:false">
//&#64;author  司徒正美|なさみ|cheng http://www.cnblogs.com/rubylouvre/  All rights reserved
      //去除两边的空白
      trim: function( text ) {
        return (text || "").replace( /^\s+|\s+$/g, "" );
      },
      //转换成数组,很大众的方法
      makeArray: function( array ) {
        var ret = [];
        if( array != null ){
          var i = array.length;
          // The window, strings (and functions) also have 'length'
          if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval )
            ret[0] = array;//就只有一元素
          else
            while( i )//处理数组
              ret[--i] = array[i];
        }

        return ret;
      },
      //判断是否在数组中,类似indexOf
      inArray: function( elem, array ) {
        for ( var i = 0, length = array.length; i &lt; length; i++ )
        // Use === because on IE, window == document
          if ( array[ i ] === elem )
            return i;

        return -1;
      },
      //把新元素或第二个数组加入第一个数组中
      //类似数组的concat
      merge: function( first, second ) {
        // We have to loop this way because IE & Opera overwrite the length
        // expando of getElementsByTagName
        var i = 0, elem, pos = first.length;
        // Also, we need to make sure that the correct elements are being returned
        // (IE returns comment nodes in a '*' query)
        if ( !jQuery.support.getAll ) {
          while ( (elem = second[ i++ ]) != null )
            if ( elem.nodeType != 8 )
              first[ pos++ ] = elem;

        } else
          while ( (elem = second[ i++ ]) != null )
            first[ pos++ ] = elem;

        return first;
      },
      //过滤重复元素,用done这个普通对象做过滤器(因为键如果同名将被覆盖掉)
      unique: function( array ) {
        var ret = [], done = {};

        try {

          for ( var i = 0, length = array.length; i &lt; length; i++ ) {
            var id = jQuery.data( array[ i ] );

            if ( !done[ id ] ) {
              done[ id ] = true;
              ret.push( array[ i ] );
            }
          }

        } catch( e ) {
          ret = array;
        }

        return ret;
      },
      //类似数组的filter,这方法起得真不好,通常这都是与正则有关的……
      //$.grep( [0,1,2], function(n,i){
      //  return n &gt; 0;
      //});
      //[1, 2]
      grep: function( elems, callback, inv ) {
        var ret = [];
        // Go through the array, only saving the items
        // that pass the validator function
        //写法很特别,callback之前的!是为了防止回调函数没有返回值
        //javascript默认没有返回值的函数都返回undefined,这样一搞
        //就变成true,原来返回true的变成false,我们需要负负得正,中和一下
        //于是!=出场了,而inv也是未必存在的,用!强制转换成布尔
        for ( var i = 0, length = elems.length; i &lt; length; i++ )
          if ( !inv != !callback( elems[ i ], i ) )
            ret.push( elems[ i ] );
        return ret;
      },
      //就是数组中的map
      map: function( elems, callback ) {
        var ret = [];

        // Go through the array, translating each of the items to their
        // new value (or values).
        for ( var i = 0, length = elems.length; i &lt; length; i++ ) {
          var value = callback( elems[ i ], i );

          if ( value != null )
            ret[ ret.length ] = value;
        }

        return ret.concat.apply( [], ret );
      }
    });

    // jQuery.browser下面的方法已经被废弃了,这些都是为兼容以前的版本与插件用

    var userAgent = navigator.userAgent.toLowerCase();

    // Figure out what browser is being used
    jQuery.browser = {
      version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
      safari: /webkit/.test( userAgent ),
      opera: /opera/.test( userAgent ),
      msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
      mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
    };
    //把以下方法parent,parents,next……添加到jQuery的原型上去,都是一些过滤方法
    jQuery.each({
      parent: function(elem){return elem.parentNode;},
      parents: function(elem){return jQuery.dir(elem,"parentNode");},
      next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
      prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
      nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
      prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
      siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
      children: function(elem){return jQuery.sibling(elem.firstChild);},
      contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
    }, function(name, fn){
      jQuery.fn[ name ] = function( selector ) {//方法体
        var ret = jQuery.map( this, fn );

        if ( selector && typeof selector == "string" )
          ret = jQuery.multiFilter( selector, ret );

        return this.pushStack( jQuery.unique( ret ), name, selector );
      };
    });
    //把以下方法appendTo,prependTo,insertBefore……添加到jQuery的原型上去,
    //利用已有的append,prepend……方法构建
    jQuery.each({
      appendTo: "append",
      prependTo: "prepend",
      insertBefore: "before",
      insertAfter: "after",
      replaceAll: "replaceWith"
    }, function(name, original){
      jQuery.fn[ name ] = function( selector ) {
        var ret = [], insert = jQuery( selector );

        for ( var i = 0, l = insert.length; i &lt; l; i++ ) {
          var elems = (i &gt; 0 ? this.clone(true) : this).get();
          jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
          ret = ret.concat( elems );
        }

        return this.pushStack( ret, name, selector );
      };
    });

     //一些重要常用的静态方法
      jQuery.each({
        removeAttr: function( name ) {
          jQuery.attr( this, name, "" );
          if (this.nodeType == 1)
            this.removeAttribute( name );
        },

        addClass: function( classNames ) {
          jQuery.className.add( this, classNames );
        },

        removeClass: function( classNames ) {
          jQuery.className.remove( this, classNames );
        },

        toggleClass: function( classNames, state ) {
          if( typeof state !== "boolean" )
            state = !jQuery.className.has( this, classNames );
          jQuery.className[ state ? "add" : "remove" ]( this, classNames );
        },

        remove: function( selector ) {
          if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
            // Prevent memory leaks
            jQuery( "*", this ).add([this]).each(function(){
              jQuery.event.remove(this);//★★★★★
              jQuery.removeData(this);
            });
            if (this.parentNode)
              this.parentNode.removeChild( this );
          }
        },

        empty: function() {
          // Remove element nodes and prevent memory leaks
          jQuery(this).children().remove();

          // Remove any remaining nodes
          while ( this.firstChild )
            this.removeChild( this.firstChild );
        }
      }, function(name, fn){
        jQuery.fn[ name ] = function(){
          return this.each( fn, arguments );
        };
      });
      //将带单位的数值去掉单位
      // Helper function used by the dimensions and offset modules
      function num(elem, prop) {
        return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
      }

</pre>
<p>接着下来看jQuery的缓存机制,jQuery的性能很大部分依仗于它。</p>
<pre class="brush:javascript;gutter:false;toolbar:false">
//&#64;author  司徒正美|RestlessDream|なさみ|cheng http://www.cnblogs.com/rubylouvre/  All rights reserved
      var expando = "jQuery" + now(), uuid = 0, windowData = {};

      jQuery.extend({
        cache: {},

        data: function( elem, name, data ) {
          //坚决不染指window
          elem = elem == window ?
            windowData :
            elem;
          //在elem上设置一个变量
          var id = elem[ expando ];

          // Compute a unique ID for the element
          if ( !id )
          //  同时为id,elem[expando]赋值,值为单一数字
            id = elem[ expando ] = ++uuid;

          // Only generate the data cache if we're
          // trying to access or manipulate it
          if ( name && !jQuery.cache[ id ] )
          //在jQuery.cache上开辟一个对象,专门用于储存与那个elem有关的东西
            jQuery.cache[ id ] = {};

          // Prevent overriding the named cache with undefined values
          if ( data !== undefined )//data必须定义
            jQuery.cache[ id ][ name ] = data;

          // Return the named cache data, or the ID for the element
          //根据第二个参数是否存在决定返回的是缓存数据还是element的特别ID
          return name ?
            jQuery.cache[ id ][ name ] :
            id;
        },
        //移除缓存数据
        removeData: function( elem, name ) {
          elem = elem == window ?
            windowData :
            elem;

          var id = elem[ expando ];

          // If we want to remove a specific section of the element's data
          if ( name ) {
            if ( jQuery.cache[ id ] ) {
              // Remove the section of cache data
              delete jQuery.cache[ id ][ name ];

              // If we've removed all the data, remove the element's cache
              name = "";

              for ( name in jQuery.cache[ id ] )
                break;

              if ( !name )
                jQuery.removeData( elem );
            }

            // Otherwise, we want to remove all of the element's data
          } else {
            // Clean up the element expando
            try {
              //IE不能直接用delete去移除,要用removeAttribute
              delete elem[ expando ];
            } catch(e){
              // IE has trouble directly removing the expando
              // but it's ok with using removeAttribute
              if ( elem.removeAttribute )
                elem.removeAttribute( expando );
            }

            // Completely remove the data cache
            //用缓存体中把其索引值也移掉
            delete jQuery.cache[ id ];
          }
        },
        //缓存元素的类组数属性
        //可读写
        queue: function( elem, type, data ) {
          if ( elem ){

            type = (type || "fx") + "queue";

            var q = jQuery.data( elem, type );

            if ( !q || jQuery.isArray(data) )
            //q是数组
              q = jQuery.data( elem, type, jQuery.makeArray(data) );
            else if( data )
              q.push( data );

          }
          return q;
        },
        //对元素的类数组缓存进行dequeue(也就是shift)
        dequeue: function( elem, type ){
          var queue = jQuery.queue( elem, type ),
          fn = queue.shift();

          if( !type || type === "fx" )
            fn = queue[0];

          if( fn !== undefined )
            fn.call(elem);
        }
      });
      //让jQuery对象也能获得这种缓存能力
      //都是用上面静态方法实现,最终的缓存体还是jQuery.cache
      jQuery.fn.extend({
        data: function( key, value ){
          var parts = key.split(".");
          parts[1] = parts[1] ? "." + parts[1] : "";

          if ( value === undefined ) {
            var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);

            if ( data === undefined && this.length )
              data = jQuery.data( this[0], key );

            return data === undefined && parts[1] ?
              this.data( parts[0] ) :
              data;
          } else
            return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
              jQuery.data( this, key, value );
            });
        },

        removeData: function( key ){
          return this.each(function(){
            jQuery.removeData( this, key );
          });
        },
        queue: function(type, data){
          if ( typeof type !== "string" ) {
            data = type;
            type = "fx";
          }

          if ( data === undefined )
            return jQuery.queue( this[0], type );

          return this.each(function(){
            var queue = jQuery.queue( this, type, data );

            if( type == "fx" && queue.length == 1 )
              queue[0].call(this);
          });
        },
        dequeue: function(type){
          return this.each(function(){
            jQuery.dequeue( this, type );
          });
        }
      });

六
<p>今天我开始攻略jQuery的心脏,css选择器。不过Sizzle是如此复杂的东西,我发现不能跟着John Resig的思路一行行读下去,因此下面的代码和jQuery的次序是不一样的。</p>

<p>jQuery的代码是包含在一个巨大的闭包中,Sizzle又在它里面开辟另一个闭包。它是完全独立于jQuery,jQuery通过find方法来调用Sizzle。一开始是这几个变量,尤其是那个正则,用于分解我们传入的字符串</p>
<pre class="brush:javascript;gutter:false;toolbar:false">
var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
	done = 0,
	toString = Object.prototype.toString;
</pre>
<p>然后我们看其表达式,用于深加工与过滤以及简单的查找:</p>
<pre class="brush:javascript;gutter:false;toolbar:false">
//&#64;author  司徒正美|なさみ|cheng http://www.cnblogs.com/rubylouvre/ All rights reserved
var Expr = Sizzle.selectors = {
	order: [ "ID", "NAME", "TAG" ],
	match: {
		ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
		CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
		NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
		ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
		TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
		CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
		POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
		PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
	},
	attrMap: {//一些属性不能直接其HTML名字去取,需要用其在javascript的属性名
		"class": "className",
		"for": "htmlFor"
	},
	attrHandle: {
		href: function(elem){
			return elem.getAttribute("href");
		}
	},
	relative: {
        //相邻选择符
		"+": function(checkSet, part, isXML){
			var isPartStr = typeof part === "string",
				isTag = isPartStr && !/\W/.test(part),
				isPartStrNotTag = isPartStr && !isTag;

			if ( isTag && !isXML ) {
				part = part.toUpperCase();
			}

			for ( var i = 0, l = checkSet.length, elem; i &lt; l; i++ ) {
				if ( (elem = checkSet[i]) ) {
					while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}

					checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
						elem || false :
						elem === part;
				}
			}

			if ( isPartStrNotTag ) {
				Sizzle.filter( part, checkSet, true );
			}
		},
        //亲子选择符
		"&gt;": function(checkSet, part, isXML){
			var isPartStr = typeof part === "string";

			if ( isPartStr && !/\W/.test(part) ) {
				part = isXML ? part : part.toUpperCase();

				for ( var i = 0, l = checkSet.length; i &lt; l; i++ ) {
					var elem = checkSet[i];
					if ( elem ) {
						var parent = elem.parentNode;
						checkSet[i] = parent.nodeName === part ? parent : false;
					}
				}
			} else {
				for ( var i = 0, l = checkSet.length; i &lt; l; i++ ) {
					var elem = checkSet[i];
					if ( elem ) {
						checkSet[i] = isPartStr ?
							elem.parentNode :
							elem.parentNode === part;
					}
				}

				if ( isPartStr ) {
					Sizzle.filter( part, checkSet, true );
				}
			}
		},
        //后代选择符
		"": function(checkSet, part, isXML){
			var doneName = done++, checkFn = dirCheck;

			if ( !part.match(/\W/) ) {
				var nodeCheck = part = isXML ? part : part.toUpperCase();
				checkFn = dirNodeCheck;
			}

			checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
		},
        //兄长选择符
		"~": function(checkSet, part, isXML){
			var doneName = done++, checkFn = dirCheck;

			if ( typeof part === "string" && !part.match(/\W/) ) {
				var nodeCheck = part = isXML ? part : part.toUpperCase();
				checkFn = dirNodeCheck;
			}

			checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
		}
	},
	find: {
		ID: function(match, context, isXML){
			if ( typeof context.getElementById !== "undefined" && !isXML ) {
				var m = context.getElementById(match[1]);
				return m ? [m] : [];//就算只有一个也放进数组
			}
		},
		NAME: function(match, context, isXML){
			if ( typeof context.getElementsByName !== "undefined" ) {
				var ret = [], results = context.getElementsByName(match[1]);

				for ( var i = 0, l = results.length; i &lt; l; i++ ) {
					if ( results[i].getAttribute("name") === match[1] ) {
						ret.push( results[i] );
					}
				}

				return ret.length === 0 ? null : ret;
			}
		},
		TAG: function(match, context){
			return context.getElementsByTagName(match[1]);
		}
	},
	preFilter: {//这里,如果符合的话都返回字符串
		CLASS: function(match, curLoop, inplace, result, not, isXML){
			match = " " + match[1].replace(/\\/g, "") + " ";

			if ( isXML ) {
				return match;
			}

			for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
				if ( elem ) {
                                       //相当于hasClassName
					if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) &gt;= 0) ) {
						if ( !inplace )
							result.push( elem );
					} else if ( inplace ) {
						curLoop[i] = false;
					}
				}
			}

			return false;
		},
		ID: function(match){
			return match[1].replace(/\\/g, "");
		},
		TAG: function(match, curLoop){
			for ( var i = 0; curLoop[i] === false; i++ ){}
			return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
		},
		CHILD: function(match){
                        //把nth(****)里面的表达式都弄成an+b的样子
			if ( match[1] == "nth" ) {
				// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
				var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
					match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
					!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);

				// calculate the numbers (first)n+(last) including if they are negative
				match[2] = (test[1] + (test[2] || 1)) - 0;
				match[3] = test[3] - 0;
			}

			// TODO: Move to normal caching system
			match[0] = done++;

			return match;
		},
		ATTR: function(match, curLoop, inplace, result, not, isXML){
			var name = match[1].replace(/\\/g, "");
			
			if ( !isXML && Expr.attrMap[name] ) {
				match[1] = Expr.attrMap[name];
			}

			if ( match[2] === "~=" ) {
				match[4] = " " + match[4] + " ";
			}

			return match;
		},
		PSEUDO: function(match, curLoop, inplace, result, not){
			if ( match[1] === "not" ) {
				// If we're dealing with a complex expression, or a simple one
				if ( match[3].match(chunker).length &gt; 1 || /^\w/.test(match[3]) ) {
					match[3] = Sizzle(match[3], null, null, curLoop);
				} else {
					var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
					if ( !inplace ) {
						result.push.apply( result, ret );
					}
					return false;
				}
			} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
				return true;
			}
			
			return match;
		},
		POS: function(match){
			match.unshift( true );
			return match;
		}
	},
	filters: {//都是返回布尔值
		enabled: function(elem){
                  //不能为隐藏域
			return elem.disabled === false && elem.type !== "hidden";
		},
		disabled: function(elem){
			return elem.disabled === true;
		},
		checked: function(elem){
			return elem.checked === true;
		},
		selected: function(elem){
			// Accessing this property makes selected-by-default
			// options in Safari work properly
			elem.parentNode.selectedIndex;
			return elem.selected === true;
		},
		parent: function(elem){
            //是否是父节点(是,肯定有第一个子节点)
			return !!elem.firstChild;
		},
		empty: function(elem){
            //是否为空,一点节点也没有
			return !elem.firstChild;
		},
		has: function(elem, i, match){
			return !!Sizzle( match[3], elem ).length;
		},
		header: function(elem){
            //是否是h1,h2,h3,h4,h5,h6
			return /h\d/i.test( elem.nodeName );
		},
		text: function(elem){
            //文本域,下面几个相仿,基本上可以归类于属性选择器
			return "text" === elem.type;
		},
		radio: function(elem){
			return "radio" === elem.type;
		},
		checkbox: function(elem){
			return "checkbox" === elem.type;
		},
		file: function(elem){
			return "file" === elem.type;
		},
		password: function(elem){
			return "password" === elem.type;
		},
		submit: function(elem){
			return "submit" === elem.type;
		},
		image: function(elem){
			return "image" === elem.type;
		},
		reset: function(elem){
			return "reset" === elem.type;
		},
		button: function(elem){
			return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
		},
		input: function(elem){
			return /input|select|textarea|button/i.test(elem.nodeName);
		}
	},
	setFilters: {//子元素过滤器
		first: function(elem, i){
			return i === 0;
		},
		last: function(elem, i, match, array){
			return i === array.length - 1;
		},
		even: function(elem, i){
			return i % 2 === 0;
		},
		odd: function(elem, i){
			return i % 2 === 1;
		},
		lt: function(elem, i, match){
			return i &lt; match[3] - 0;
		},
		gt: function(elem, i, match){
			return i &gt; match[3] - 0;
		},
		nth: function(elem, i, match){
			return match[3] - 0 == i;
		},
		eq: function(elem, i, match){
			return match[3] - 0 == i;
		}
	},
	filter: {
		PSEUDO: function(elem, match, i, array){
			var name = match[1], filter = Expr.filters[ name ];

			if ( filter ) {
				return filter( elem, i, match, array );
			} else if ( name === "contains" ) {
				return (elem.textContent || elem.innerText || "").indexOf(match[3]) &gt;= 0;
			} else if ( name === "not" ) {
				var not = match[3];

				for ( var i = 0, l = not.length; i &lt; l; i++ ) {
					if ( not[i] === elem ) {
						return false;
					}
				}

				return true;
			}
		},
		CHILD: function(elem, match){
			var type = match[1], node = elem;
			switch (type) {
				case 'only':
				case 'first':
					while (node = node.previousSibling)  {
						if ( node.nodeType === 1 ) return false;
					}
					if ( type == 'first') return true;
					node = elem;
				case 'last':
					while (node = node.nextSibling)  {
						if ( node.nodeType === 1 ) return false;
					}
					return true;
				case 'nth':
					var first = match[2], last = match[3];

					if ( first == 1 && last == 0 ) {
						return true;
					}
					
					var doneName = match[0],
						parent = elem.parentNode;
	
					if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
						var count = 0;
						for ( node = parent.firstChild; node; node = node.nextSibling ) {
							if ( node.nodeType === 1 ) {
								node.nodeIndex = ++count;//添加一个私有属性
							}
						} 
						parent.sizcache = doneName;
					}
					
					var diff = elem.nodeIndex - last;
					if ( first == 0 ) {
						return diff == 0;//判断是否为第一个子元素
					} else {
						return ( diff % first == 0 && diff / first &gt;= 0 );
					}
			}
		},
		ID: function(elem, match){
			return elem.nodeType === 1 && elem.getAttribute("id") === match;
		},
		TAG: function(elem, match){
			return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
		},
		CLASS: function(elem, match){
			return (" " + (elem.className || elem.getAttribute("class")) + " ")
				.indexOf( match ) &gt; -1;
		},
		ATTR: function(elem, match){
			var name = match[1],
				result = Expr.attrHandle[ name ] ?
					Expr.attrHandle[ name ]( elem ) :
					elem[ name ] != null ?
						elem[ name ] :
						elem.getAttribute( name ),
				value = result + "",
				type = match[2],
				check = match[4];

			return result == null ?
				type === "!=" :
				type === "=" ?
				value === check :
				type === "*=" ?
				value.indexOf(check) &gt;= 0 :
				type === "~=" ?
				(" " + value + " ").indexOf(check) &gt;= 0 :
				!check ?
				value && result !== false :
				type === "!=" ?
				value != check :
				type === "^=" ?
				value.indexOf(check) === 0 :
				type === "$=" ?
				value.substr(value.length - check.length) === check :
				type === "|=" ?
				value === check || value.substr(0, check.length + 1) === check + "-" :
				false;
		},
		POS: function(elem, match, i, array){
			var name = match[2], filter = Expr.setFilters[ name ];

			if ( filter ) {
				return filter( elem, i, match, array );
			}
		}
	}
};

var origPOS = Expr.match.POS;


</pre>
<div><img src="http://images.cnblogs.com/cnblogs_com/rubylouvre/205314/o_Sizzle.gif"></div>
<p>但上图没有完全显现Sizzle复杂的工作机制,它是从左到右工作,加工了一个字符串,查找,然后过滤非元素节点,再跟据其属性或内容或在父元素的顺序过滤,然后到下一个字符串,这时搜索起点就是上次的结果数组的元素节点,想象一下草根的样子吧。在许多情况下,选择器都是靠工作的,element.getElementsByTagName(*),获得其一元素的所有子孙,因此Expr中的过滤器特别多。为了过快查找速度,如有些浏览器已经实现了getElementsByClassName,jQuery也设法把它们利用起来。</p>
<pre class="brush:javascript;gutter:false;toolbar:false">
for ( var type in Expr.match ) {
    //重写Expr.match中的正则,利用负向零宽断言让其更加严谨
	Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
}
</pre>
<p>接着下来我们还是未到时候看上面的主程序,继续看它的辅助方法。</p>
<pre class="brush:javascript;gutter:false;toolbar:false">
//把NodeList HTMLCollection转换成纯数组,如果有第二参数(上次查找的结果),则把它们加入到结果集中
var makeArray = function(array, results) {
	array = Array.prototype.slice.call( array );

	if ( results ) {
		results.push.apply( results, array );
		return results;
	}
	
	return array;
};


try {
    //基本上是用于测试IE的,IE的NodeList HTMLCollection不支持用数组的slice转换为数组
	Array.prototype.slice.call( document.documentElement.childNodes );

//这时就要重载makeArray,一个个元素搬入一个空数组中了
} catch(e){
	makeArray = function(array, results) {
		var ret = results || [];

		if ( toString.call(array) === "[object Array]" ) {
			Array.prototype.push.apply( ret, array );
		} else {
			if ( typeof array.length === "number" ) {
				for ( var i = 0, l = array.length; i &lt; l; i++ ) {
					ret.push( array[i] );
				}
			} else {
				for ( var i = 0; array[i]; i++ ) {
					ret.push( array[i] );
				}
			}
		}

		return ret;
	};
}

 
七
<p>在Sizzle中有许多有用的辅助方法,我们继续一个个看。其中涉及许多BUG的修正以及一些很少见的API。</p>

<pre class="brush:javascript;gutter:false;toolbar:false">
//&#64;author  司徒正美|なさみ|cheng http://www.cnblogs.com/rubylouvre/ All rights reserved
      var sortOrder;//比较两个元素在页面上的顺序,返回正数,0,负数
      //如果支持compareDocumentPosition方法,新锐的标准浏览器都支持
      //我在《javascript contains方法》一文中有详细介绍
      //http://www.cnblogs.com/rubylouvre/archive/2009/10/14/1583523.html
      if ( document.documentElement.compareDocumentPosition ) {
        sortOrder = function( a, b ) {
          //节点a 在节点b 之前,
          var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
          if ( ret === 0 ) {
            hasDuplicate = true;
          }
          return ret;
        };
        //用于IE
        //sourceIndex是指元素在NodeList中的位置
      } else if ( "sourceIndex" in document.documentElement ) {
        sortOrder = function( a, b ) {
          var ret = a.sourceIndex - b.sourceIndex;
          if ( ret === 0 ) {
            hasDuplicate = true;
          }
          return ret;
        };
        //用于旧式的标准游览器
      } else if ( document.createRange ) {
        sortOrder = function( a, b ) {
          var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
          aRange.selectNode(a);
          aRange.collapse(true);
          bRange.selectNode(b);
          bRange.collapse(true);
          //比较两个selection的位置
          //https://developer.mozilla.org/en/DOM/range.compareBoundaryPoints
          var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
          if ( ret === 0 ) {
            hasDuplicate = true;
          }
          return ret;
        };
      }
</pre>
<p>比较元素位置在IE还可以用uniqueNumber,都是自上至下分配数字。</p>
<p>下面对getElementById,getElementsByTagName,getElementsByClassName, querySelectorAll 进行调整。</p>
<pre class="brush:javascript;gutter:false;toolbar:false">
//在getElementById(XXX)在IE中有bug,它会找第一个属性name或id等于XXX的元素,
//尤其是在表单元素中,它们通常都带有name属性
(function(){
	// We're going to inject a fake input element with a specified name
	var form = document.createElement("form"),
		id = "script" + (new Date).getTime();
	form.innerHTML = "&lt;input name='" + id + "'/&gt;";

	// Inject it into the root element, check its status, and remove it quickly
	var root = document.documentElement;
	root.insertBefore( form, root.firstChild );

	// The workaround has to do additional checks after a getElementById
	// Which slows things down for other browsers (hence the branching)
	if ( !!document.getElementById( id ) ) {
        //重载一下Expr.find.ID
		Expr.find.ID = function(match, context, isXML){
			if ( typeof context.getElementById !== "undefined" && !isXML ) {
				var m = context.getElementById(match[1]);
                //确定此元素是否显式为id赋值
				return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" &&
                    m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
			}
		};

		Expr.filter.ID = function(elem, match){
             //确定此元素是否显式为id赋值
			var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
			return elem.nodeType === 1 && node && node.nodeValue === match;
		};
	}

	root.removeChild( form );
})();

(function(){
	// Check to see if the browser returns only elements
	// when doing getElementsByTagName("*")

	// Create a fake element
	var div = document.createElement("div");
	div.appendChild( document.createComment("") );

	// Make sure no comments are found
	if ( div.getElementsByTagName("*").length &gt; 0 ) {
        //重载Expr.find.TAG
		Expr.find.TAG = function(match, context){
			var results = context.getElementsByTagName(match[1]);

			// Filter out possible comments
            //返回其所有元素节点后代,组成纯数组
			if ( match[1] === "*" ) {
				var tmp = [];

				for ( var i = 0; results[i]; i++ ) {
					if ( results[i].nodeType === 1 ) {
						tmp.push( results[i] );
					}
				}

				results = tmp;
			}

			return results;
		};
	}

	// Check to see if an attribute returns normalized href attributes
    //处理href属性,如果第二个参数,IE返回的是绝对路径
	div.innerHTML = "&lt;a href='#'&gt;&lt;/a&gt;";
	if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
			div.firstChild.getAttribute("href") !== "#" ) {
		Expr.attrHandle.href = function(elem){
			return elem.getAttribute("href", 2);
		};
	}
})();

if ( document.querySelectorAll ) (function(){
    //创建一个元素片段&lt;div&gt;&lt;p class='TEST'&gt;&lt;/p&gt;&lt;/div&gt;

    //用querySelectorAll看看能否正确找到这个p元素
	var oldSizzle = Sizzle, div = document.createElement("div");
	div.innerHTML = "&lt;p class='TEST'&gt;&lt;/p&gt;";

	// Safari can't handle uppercase or unicode characters when
	// in quirks mode.
	if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
		return;
	}
	//如果能,就用querySelectorAll重载整个Sizzle引擎,效率最高!!!
	Sizzle = function(query, context, extra, seed){
		context = context || document;

		// Only use querySelectorAll on non-XML documents
		// (ID selectors don't work in non-HTML documents)
		if ( !seed && context.nodeType === 9 && !isXML(context) ) {
			try {
				return makeArray( context.querySelectorAll(query), extra );
			} catch(e){}
		}
		
		return oldSizzle(query, context, extra, seed);
	};

	Sizzle.find = oldSizzle.find;
	Sizzle.filter = oldSizzle.filter;
	Sizzle.selectors = oldSizzle.selectors;
	Sizzle.matches = oldSizzle.matches;
})();

if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
     //创建一个元素片段&lt;div&gt;&lt;div class='test e'&gt;&lt;/div&gt;&lt;div class='test'&gt;&lt;/div&gt;&lt;/div&gt;

    //用getElementsByClassName看看能否正确找到这两个div元素
	var div = document.createElement("div");
	div.innerHTML = "&lt;div class='test e'&gt;&lt;/div&gt;&lt;div class='test'&gt;&lt;/div&gt;";

	// Opera can't find a second classname (in 9.6)
	if ( div.getElementsByClassName("e").length === 0 )
		return;

	// Safari caches class attributes, doesn't catch changes (in 3.2)
	div.lastChild.className = "e";

	if ( div.getElementsByClassName("e").length === 1 )
		return;
//重新调整与CLASS有关的逻辑
	Expr.order.splice(1, 0, "CLASS");
	Expr.find.CLASS = function(match, context, isXML) {
		if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
			return context.getElementsByClassName(match[1]);
		}
	};
})();
</pre>
<pre class="brush:javascript;gutter:false;toolbar:false">
//这东西用于后代选择器与兄长选择器,取得某范围中所有元素,并且防止重复取得
function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
	var sibDir = dir == "previousSibling" && !isXML;
    //checkSet为元素集合,doneName为数字
	for ( var i = 0, l = checkSet.length; i &lt; l; i++ ) {
		var elem = checkSet[i];
		if ( elem ) {
			if ( sibDir && elem.nodeType === 1 ){
				elem.sizcache = doneName;//设置一标记,以后有与它值相等的不重复取
				elem.sizset = i;
			}
			elem = elem[dir];
			var match = false;

			while ( elem ) {
				if ( elem.sizcache === doneName ) {//比较是否相等
					match = checkSet[elem.sizset];
					break;
				}

				if ( elem.nodeType === 1 && !isXML ){
					elem.sizcache = doneName;
					elem.sizset = i;
				}

				if ( elem.nodeName === cur ) {
					match = elem;
					break;
				}

				elem = elem[dir];
			}

			checkSet[i] = match;
		}
	}
}
//和上面功能差不多,不知是否出于兼容以前版本的需要……
function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
	var sibDir = dir == "previousSibling" && !isXML;
	for ( var i = 0, l = checkSet.length; i &lt; l; i++ ) {
		var elem = checkSet[i];
		if ( elem ) {
			if ( sibDir && elem.nodeType === 1 ) {
				elem.sizcache = doneName;
				elem.sizset = i;
			}
			elem = elem[dir];
			var match = false;

			while ( elem ) {
				if ( elem.sizcache === doneName ) {
					match = checkSet[elem.sizset];
					break;
				}

				if ( elem.nodeType === 1 ) {
					if ( !isXML ) {
						elem.sizcache = doneName;
						elem.sizset = i;
					}
					if ( typeof cur !== "string" ) {
						if ( elem === cur ) {
							match = true;
							break;
						}

					} else if ( Sizzle.filter( cur, [elem] ).length &gt; 0 ) {
						match = elem;
						break;
					}
				}

				elem = elem[dir];
			}

			checkSet[i] = match;
		}
	}
}

</pre>
<pre class="brush:javascript;gutter:false;toolbar:false">
        //判断一个元素是否包含另一个元素
        //http://www.cnblogs.com/rubylouvre/archive/2009/10/14/1583523.html
        var contains = document.compareDocumentPosition ?  function(a, b){
            return a.compareDocumentPosition(b) & 16;
        } : function(a, b){
            return a !== b && (a.contains ? a.contains(b) : true);
        };
        //判断是否为XML
        var isXML = function(elem){
            return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
            !!elem.ownerDocument && isXML( elem.ownerDocument );
        };
        //主要是处理结构伪类中的子元素过滤器
        var posProcess = function(selector, context){
            var tmpSet = [], later = "", match,
            root = context.nodeType ? [context] : context;

            // Position selectors must be done after the filter
            // And so must :not(positional) so we move all PSEUDOs to the end
            while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
                later += match[0];
                selector = selector.replace( Expr.match.PSEUDO, "" );
            }
            //如果不是在亲子中选择,就是在它的所有后代中选择“*”
            selector = Expr.relative[selector] ? selector + "*" : selector;
            //回调Sizzle
            for ( var i = 0, l = root.length; i < l; i++ ) {
                Sizzle( selector, root[i], tmpSet );
            }

            return Sizzle.filter( later, tmpSet );
        };

八
<p>今天把jQuery的Sizzle选择器引擎讲完。最后给出其大体的工作流程。这东西非常复杂,不要妄图看一遍就明白了。无论看懂与否,多看点源码,还是有裨益的。至少在处理循环结构上有收获吧。</p>

<pre class="brush:javascript;gutter:false;toolbar:false">  
//&#64;author  司徒正美|なさみ|cheng http://www.cnblogs.com/rubylouvre/ All rights reserved
    // EXPOSE
      jQuery.find = Sizzle;
      jQuery.filter = Sizzle.filter;
      jQuery.expr = Sizzle.selectors;
      //以:开头许多都是自定义伪类
      jQuery.expr[":"] = jQuery.expr.filters;
      //css属性display引起的元素不可见
      Sizzle.selectors.filters.hidden = function(elem){
        return elem.offsetWidth === 0 || elem.offsetHeight === 0;
      };
      //css属性display引起的元素不可见
      Sizzle.selectors.filters.visible = function(elem){
        return elem.offsetWidth &gt; 0 || elem.offsetHeight &gt; 0;
      };
      //是否在运动中
      Sizzle.selectors.filters.animated = function(elem){
        return jQuery.grep(jQuery.timers, function(fn){
          return elem === fn.elem;
        }).length;
      };
      //重载jQuery.multiFilter
      jQuery.multiFilter = function( expr, elems, not ) {
        if ( not ) {
          expr = ":not(" + expr + ")";
        }

        return Sizzle.matches(expr, elems);
      };
      //把路径上的元素放到结果上,dir为parentNode,previousSibling,nextSilbing
      jQuery.dir = function( elem, dir ){
        var matched = [], cur = elem[dir];
        while ( cur && cur != document ) {
          if ( cur.nodeType == 1 )
            matched.push( cur );
          cur = cur[dir];
        }
        return matched;
      };
      //在内部调用result好像都为2,dir为previousSibling,nextSilbing
      //用于子元素过滤
      jQuery.nth = function(cur, result, dir, elem){
        result = result || 1;
        var num = 0;
        //如果cur为undefined中止循环
        for ( ; cur; cur = cur[dir] )
          if ( cur.nodeType == 1 && ++num == result )
            break;

        return cur;
      };
      //查找不等于elem的兄弟元素节点
      jQuery.sibling = function(n, elem){
        var r = [];

        for ( ; n; n = n.nextSibling ) {
          if ( n.nodeType == 1 && n != elem )
            r.push( n );
        }

        return r;
      };

      return;

      window.Sizzle = Sizzle;
</pre>
<p>好了,回头看Sizzle的主程序部分:</p>
<pre class="brush:javascript;gutter:false;toolbar:false">  
        Sizzle.find = function(expr, context, isXML){
            var set, match;
            if ( !expr ) {//如果不是字符串表达式则返回空数组
                return [];
            }
            for ( var i = 0, l = Expr.order.length; i &lt; l; i++ ) {
                var type = Expr.order[i], match;//按照ID NAME TAG的优先级顺序执行
               //这里可以想象一下
               //match = "#aaa".exec( /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/)
               //然后检测match是否为空数组,空数组相当于false
                if ( (match = Expr.match[ type ].exec( expr )) ) {
                 //ID的正则 /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/
                    var left = RegExp.leftContext
                    //如果不是一步到位,是复杂的表达式,需要多次查找与筛选
                    if ( left.substr( left.length - 1 ) !== "\\" ) {
                      //把换行符去掉,得到正常的字段
                      //如"#id12\
                      //34"
                      //去掉后,就得到"#id1234"
                        match[1] = (match[1] || "").replace(/\\/g, "");
                        set = Expr.find[ type ]( match, context, isXML );
                        if ( set != null ) {
                          //移除相应部分的表达,
                          // 如#aaa ee,得到ID对应的元素后,把#aaa去掉,
                          //然后用Expr的表达式来匹配剩下的部分
                            expr = expr.replace( Expr.match[ type ], "" );
                            break;
                        }
                    }
                }
            }

            if ( !set ) {
                //返回所有后代
                set = context.getElementsByTagName("*");
            }

            return {//返回一个对象
                set: set,
                expr: expr
            };
        };

</pre>
<pre class="brush:javascript;gutter:false;toolbar:false">  
      Sizzle.filter = function(expr, set, inplace, not){
        var old = expr, result = [], curLoop = set, match, anyFound,
        isXMLFilter = set && set[0] && isXML(set[0]);

        while ( expr && set.length ) {
          for ( var type in Expr.filter ) {
            //这是Expr.filter中的键值对
            //PSEUDO: function(elem, match, i, array){},
            //CHILD: function(elem, match){},
            //ID: function(elem, match){},
            //TAG: function(elem, match){},
            //CLASS: function(elem, match){},
            //ATTR: function(elem, match){},
            //POS: function(elem, match, i, array){}
            if ( (match = Expr.match[ type ].exec( expr )) != null ) {//match为数组
              var filter = Expr.filter[ type ], found, item;//filter这函数
              anyFound = false;

              if ( curLoop == result ) {//如果结果集为空数组,就让result = [];
                result = [];
              }

              if ( Expr.preFilter[ type ] ) {
                //这是Expr.preFilter中的键值对
                //CLASS: function(match, curLoop, inplace, result, not, isXML){},
                //ID: function(match){},
                //TAG: function(match, curLoop){},
                //CHILD: function(match){ },
                //ATTR: function(match, curLoop, inplace, result, not, isXML){},
                //PSEUDO: function(match, curLoop, inplace, result, not){ },
                //POS: function(match){}
                //preFilter与filter的功能不同,preFilter对字符串进行调整,好让选择器能找到元素
                //filter对查找到的元素或元素数组进行筛选
                match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
                if ( !match ) {//如果返回的是false
                  anyFound = found = true;//就把anyFound与found标记为true
                } else if ( match === true ) {
                  continue;
                }
              }

              if ( match ) {
                for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
                  if ( item ) {
                    //检测元素是否符合要求
                    found = filter( item, match, i, curLoop );
                    var pass = not ^ !!found;

                    if ( inplace && found != null ) {
                      if ( pass ) {
                        anyFound = true;
                      } else {
                        curLoop[i] = false;
                      }
                    } else if ( pass ) {
                      result.push( item );//符合要求就放到结果数组中
                      anyFound = true;
                    }
                  }
                }
              }

              if ( found !== undefined ) {
                if ( !inplace ) {
                  curLoop = result;//结果数组将作为一下次要遍历的元素集合返回
                }
                //移除用户输入字符串已查找了的那一部分表达式
                expr = expr.replace( Expr.match[ type ], "" );

                if ( !anyFound ) {
                  return [];
                }

                break;
              }
            }
          }

          // Improper expression
          if ( expr == old ) {
            if ( anyFound == null ) {
              throw "Syntax error, unrecognized expression: " + expr;
            } else {
              break;
            }
          }

          old = expr;
        }

        return curLoop;
      };
</pre>
<p>主程序:</p>
<pre class="brush:javascript;gutter:false;toolbar:false">
      var Sizzle = function(selector, context, results, seed) {
        results = results || [];
        context = context || document;

        if ( context.nodeType !== 1 && context.nodeType !== 9 )
          return [];//context必须为DOM元素或document,要不返回空数组

        if ( !selector || typeof selector !== "string" ) {
          return results;//selector必须存在并且为字符串,否则返回上次循环的结果集
        }

        var parts = [], m, set, checkSet, check, mode, extra, prune = true;

        // Reset the position of the chunker regexp (start from head)
        chunker.lastIndex = 0;

        while ( (m = chunker.exec(selector)) !== null ) {
          parts.push( m[1] );

          if ( m[2] ) {
            extra = RegExp.rightContext;//匹配内容的右边归入extra
            break;
          }
        }
        //POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
        if ( parts.length &gt; 1 && origPOS.exec( selector ) ) {
          //处理E F   E &gt; F    E + F   E ~ F
          if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
            //这里的parts[0]肯定不是“”,亦即不会是后代选择器
            set = posProcess( parts[0] + parts[1], context );
          } else {
            set = Expr.relative[ parts[0] ] ?
              [ context ] :
              Sizzle( parts.shift(), context );

            while ( parts.length ) {
              selector = parts.shift()

              if ( Expr.relative[ selector ] )
                selector += parts.shift();

              set = posProcess( selector, set );
            }
          }
        } else {
          var ret = seed ?
            {
            expr: parts.pop(),
            set: makeArray(seed)
          } :
            Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
          set = Sizzle.filter( ret.expr, ret.set );

          if ( parts.length &gt; 0 ) {
            checkSet = makeArray(set);
          } else {
            prune = false;
          }

          while ( parts.length ) {//倒序的while循环比for循环快
            var cur = parts.pop(), pop = cur;

            if ( !Expr.relative[ cur ] ) {
              cur = "";
            } else {
              pop = parts.pop();
            }

            if ( pop == null ) {
              pop = context;
            }

            Expr.relative[ cur ]( checkSet, pop, isXML(context) );
          }
        }

        if ( !checkSet ) {
          checkSet = set;
        }

        if ( !checkSet ) {
          throw "Syntax error, unrecognized expression: " + (cur || selector);
        }
        //数组化NodeList,并加入结果集中
        if ( toString.call(checkSet) === "[object Array]" ) {
          if ( !prune ) {
            results.push.apply( results, checkSet );
          } else if ( context.nodeType === 1 ) {
            for ( var i = 0; checkSet[i] != null; i++ ) {
              if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
                results.push( set[i] );
              }
            }
          } else {
            for ( var i = 0; checkSet[i] != null; i++ ) {
              if ( checkSet[i] && checkSet[i].nodeType === 1 ) {//确保是元素节点
                results.push( set[i] );
              }
            }
          }
        } else {
          makeArray( checkSet, results );
        }

        if ( extra ) {
          Sizzle( extra, context, results, seed );
          if ( sortOrder ) {
            hasDuplicate = false;
            results.sort(sortOrder);//重排结果集中的DOM元素,按照原来在网页先后顺序排列
            if ( hasDuplicate ) {
              for ( var i = 1; i &lt; results.length; i++ ) {//确保没有重复的DOM元素,方法比较垃圾
                if ( results[i] === results[i-1] ) {
                  results.splice(i--, 1);
                }
              }
            }
          }
        }

        return results;
      };

</pre>
<p>最后重新说一下其逻辑:</p>
<ol>
<li>首先用一个叫chunker的强大正则,把诸如 var str = " #div  ,  h1#id\<br />
dd.class > span[dd='22222 > 3233'] ul+ li,  .class:contain(\"你的+ 999\"),strong span ";这样的字符串,Sizzle称之为selector的东西,分解成一个数组。<img src="http://images.cnblogs.com/cnblogs_com/rubylouvre/202680/o_selectors.gif" /></li>
<li>接着对上下文的内容进行判断,确保其为DOM元素或document,否则返回空数组。然后判断selector是否为字符串,由于Sizzle会不断递归调用,selector会越来越短的,直到为零。这些越来越短的selector其实也是第一次chunker 分解的结果之一。不过它们都有可能g再遭分解。每一次循环,这些分解了的字符串都会经过筛选(非空字符),放入parts数组中。</li>
<li>这些selector最先会判断一下,是否为亲子兄长相邻后代等关系选择器。由于第一次chunker把大部分空白消灭了,造成了一个不幸的结果,把后代选择器也消灭了。因此必须补上后代选择器。详见后面posProcess的“selector + "*"”操作。</li>
<li>在选择器中,也亦即id,tag,name具有查找能力,在标准浏览器中重载了class部分,让getElementsByClassName也能工作。如果querySelectorAll能工作最好不过,整个Sizzle被重载了。总而言之,Sizzle.find所做的工作比较少,它是按[ "ID", "NAME", "TAG" ]的优先级查找元素的。不过在这之前,先要调用Expr.preFilter把连字符"\"造成的字符串破坏进行修复了。如上面的例子,h1#iddd由于中间的连字符串被切成两个部分,成了数组中的两个元素h1#dd与dd。显然这样查找会找不到dd这个ID,后面查找所有dd元素也是错误的,因此必须把它们重新整合成一个元素h1#dddd。</li>
<li>根据id,name与tag找到这些元素后,下一个循环就是找它们的子元素或后代元素了,所以Sizzle才会急冲冲地修复后代选择器的问题。至于筛选,Expr有大量的方法来进行。最后是重新排序与去除重复选中的元素,以结果集返回。</li>
</ol>
 
 
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics