jquery 源码分析六 - Sizzle
以下吐槽:
最近一直在面试啊找工作啊,然后学校里又有一堆事情,结果看代码的时间变少了。憋了好几天终于感觉可以写一篇文章了,早上就爬起来赶紧写下来,怕又忘了。吐槽下:小本科找工作真的好难。。。。
正文开始:
首先是Sizzle里面的一些基础方法,也可是说是用的比较多的东西。因为Sizzle一开始就定义了许许多多的变量,所以这边先讲一些这次会用到的变量(比较简单的。。)
1 expando = "sizzle" + -(new Date()), 2 //给每个Sizzle生成一个特征码,区别每个sizzle 3 preferredDoc = window.document, 4 //指向document 5 dirruns = 0, 6 done = 0, 7 classCache = createCache(), 8 tokenCache = createCache(), 9 compilerCache = createCache(), 10 //这个制造了一个存储的空间,具体的createCache函数会在下面讲 11 sortOrder = function( a, b ) { 12 if ( a === b ) { 13 hasDuplicate = true; 14 } 15 return 0; 16 }, 17 18 // 未定义 19 strundefined = typeof undefined, 20 MAX_NEGATIVE = 1 << 31, 21 22 // 定义原生的方法,方便后面快速使用 23 hasOwn = ({}).hasOwnProperty, 24 arr = [], 25 pop = arr.pop, 26 push_native = arr.push, 27 push = arr.push, 28 slice = arr.slice, 29 // 如果原生的indexOf没有的话,就用自己写得函数 30 indexOf = arr.indexOf || function( elem ) { 31 var i = 0, 32 len = this.length; 33 for ( ; i < len; i++ ) { 34 if ( this[i] === elem ) { 35 return i; 36 } 37 } 38 return -1; 39 },
注意:这里的代码都是在开头var里面截取出来的,所以后面都是逗号。。。
下面是关于createCache(),createCache返回的是一个function,这个函数接受两个参数,分别是key,value。key值会存入keys 数组中,然后value会放到这个函数作为object形式的相应key值下,具体代码如下:
1 function createCache() { 2 var keys = []; 3 4 function cache( key, value ) { 5 // 用key+ " "来防止覆盖原生的一些方法 6 if ( keys.push( key + " " ) > Expr.cacheLength ) { 7 //若超过数量限制,就把最前面的一些数弹出 8 delete cache[ keys.shift() ]; 9 } 10 return (cache[ key + " " ] = value); 11 } 12 return cache; 13 }
接下来是一些正则表达式,备份正则表达式还没理解,所以有注释的只是其中部分:
1 //用于检测空格符,包括回车之类的 2 whitespace = "[\\x20\\t\\r\\n\\f]", 3 // 检测由\\.或字符-或非ASCII从00到a0的值 4 characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", 5 6 // 这个还没理解,英文翻译了也没看懂。。但是应该是用来匹配css的 7 // 一个未被应用的值应该是css标识符http://www.w3.org/TR/css3-selectors/#attribute-selectors 8 // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier 9 identifier = characterEncoding.replace( "w", "w#" ), 10 11 // 用来匹配attribute的,有点长。。。 12 attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + 13 "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", 14 15 // Prefer arguments quoted, 16 // then not containing pseudos/brackets, 17 // then attribute selectors/non-parenthetical expressions, 18 // then anything else 19 // These preferences are here to reduce the number of selectors 20 // needing tokenize in the PSEUDO preFilter 21 pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", 22 23 // 检测字符串首尾空白符用的 24 rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), 25 //用来检测逗号前后的空白的 26 rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), 27 rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), 28 29 rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), 30 31 rpseudo = new RegExp( pseudos ), 32 ridentifier = new RegExp( "^" + identifier + "$" ), 33 //根据不同的属性建立不同的正则,后面可以分离检测不同的css selector部分 34 matchExpr = { 35 "ID": new RegExp( "^#(" + characterEncoding + ")" ), 36 "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), 37 "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), 38 "ATTR": new RegExp( "^" + attributes ), 39 "PSEUDO": new RegExp( "^" + pseudos ), 40 "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + 41 "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + 42 "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), 43 "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), 44 // 用于 .is() 45 "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + 46 whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) 47 }, 48 //input类型检测 49 rinputs = /^(?:input|select|textarea|button)$/i, 50 rheader = /^h\d$/i, 51 //是否有原生代码 52 rnative = /^[^{]+\{\s*\[native \w/, 53 54 // 快速简单的检测ID TAG CLASS 55 rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, 56 57 rsibling = /[+~]/, 58 rescape = /'|\\/g, 59 60 // 没搞懂。。。CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters 61 runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), 62 funescape = function( _, escaped, escapedWhitespace ) { 63 var high = "0x" + escaped - 0x10000; 64 // NaN means non-codepoint 65 // Support: Firefox 66 // Workaround erroneous numeric interpretation of +"0x" 67 return high !== high || escapedWhitespace ? 68 escaped : 69 high < 0 ? 70 // BMP codepoint 71 String.fromCharCode( high + 0x10000 ) : 72 // Supplemental Plane codepoint (surrogate pair) 73 String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); 74 };
还有一段关于push那些NodeList的bug修复,在Android小于4.0的浏览器中,push会静默的失败,所以为了兼容这个,就需要写一个自己的push方法,jquery代码如下:
try { push.apply( (arr = slice.call( preferredDoc.childNodes )), preferredDoc.childNodes ); // 检测Android<4.0 // push静默的失败,下面这句会抛出错误 arr[ preferredDoc.childNodes.length ].nodeType; } catch ( e ) { push = { apply: arr.length ? // 尽量使用原生的push function( target, els ) { push_native.apply( target, slice.call(els) ); } : // 对于IE9 // 用自己的方法,直接添加到后面 function( target, els ) { var j = target.length, i = 0; // 需要自己设置length,length不可靠 while ( (target[j++] = els[i++]) ) {} target.length = j - 1; } }; }
下一篇会分析的是Sizzle比较核心的代码,已经看了一大部分了,会尽快发上来,不会拖延很长时间的(谁来拯救我的拖延症。。)