Sizzle一步步实现所有功能(层级选择)

第二步:实现Sizzle("el,el,el..."),Sizzle("el > el"),Sizzle("el el"),Sizzle("el + el"),Sizzle("el ~ el")

  1 (function( window ){

  2       

  3 var arr = [];

  4 var select ;

  5 var Expr;

  6 var push = arr.push;

  7 // http://www.w3.org/TR/css3-selectors/#whitespace

  8 // 各种空白待穿正则字符串

  9 var whitespace = "[\\x20\\t\\r\\n\\f]";

 10 // 带空格选择器正则,记忆无空格选择器

 11 // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier

 12 var    identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+";

 13 // 属性选择器: http://www.w3.org/TR/selectors/#attribute-selectors

 14 var    attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +

 15         // Operator (capture 2)

 16         "*([*^$|!~]?=)" + whitespace +

 17         // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"

 18         "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +

 19         "*\\]";

 20 var rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" );

 21 // 快速选择器正则 ID 或者 TAG(包括*) 或者 CLASS 选择器

 22 var rquickExpr = /^(?:#([\w-]+)|(\w+|\*)|\.([\w-]+))$/;

 23 // 连接符号

 24 var rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" );

 25 // 层级符号正则'>',' ','+','~'

 26 var rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" );

 27 var matchExpr = {

 28         "ID": new RegExp( "^#(" + identifier + ")" ),

 29         "CLASS": new RegExp( "^\\.(" + identifier + ")" ),

 30         "TAG": new RegExp( "^(" + identifier + "|[*])" ),

 31 };

 32 // 浏览器代码正则

 33 var rnative = /^[^{]+\{\s*\[native \w/;

 34 // token缓存

 35 var tokenCache = createCache();

 36 // 编译缓存

 37 var compilerCache = createCache();

 38 // 入口

 39 function Sizzle( selector ){

 40     // 清除空格

 41     selector = selector.replace( rtrim, "$1" )

 42     var results = [];

 43     var match;

 44     var matcher;

 45     var elem;

 46     var m;

 47     var context = document;

 48     

 49     // 是否为最简选择器

 50     if( match = rquickExpr.exec( selector )){

 51         // Sizzle('#ID)

 52         if ( (m = match[1]) ) {

 53             elem = context.getElementById( m );

 54             if( elem ){

 55                 results.push( elem );

 56             }

 57             return results;

 58             

 59         // Sizzle("TAG")

 60         }else if( (m = match[2]) ){

 61             push.apply( results, context.getElementsByTagName( selector ) );

 62             return results;

 63         

 64         // Sizzle(".CLASS")    

 65         }else if( (m = match[3]) ){

 66             // 支持getElementsByClassName

 67             if( support.getElementsByClassName ){

 68                 push.apply( results, context.getElementsByClassName( m ) );

 69                 return results;

 70             }

 71         }

 72     }

 73     // 复杂选择调到select

 74     return select( selector, context, results);

 75 }

 76 // 创建缓存函数

 77 function createCache() {

 78     var keys = [];

 79 

 80     function cache( key, value ) {

 81         // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)

 82         if ( keys.push( key + " " ) > 10 ) {

 83             // Only keep the most recent entries

 84             delete cache[ keys.shift() ];

 85         }

 86         return (cache[ key + " " ] = value);

 87     }

 88     return cache;

 89 }

 90 // 错误函数

 91 Sizzle.error = function( msg ) {

 92     throw new Error( "Syntax error, unrecognized expression: " + msg );

 93 };

 94 // 版本支持变量的对外访问入口

 95 var support = Sizzle.support = {};

 96 

 97 // 判断是否支持getElementsByClassName

 98 // 支持: IE<9

 99 support.getElementsByClassName = rnative.test( document.getElementsByClassName );

100 // 表达式对象

101 // 存放各类相对位置,各种查询函数,各种过滤函数等。

102 Expr = {

103     relative: {

104         ">": { dir: "parentNode", first: true },

105         " ": { dir: "parentNode" },

106         "+": { dir: "previousSibling", first: true },

107         "~": { dir: "previousSibling" }

108     },

109     filter: {

110         "TAG": function( nodeNameSelector ) {

111             var nodeName = nodeNameSelector.toLowerCase();

112             return nodeNameSelector === "*" ?

113                 function() { return true; } :

114                 function( elem ) {

115                     return elem.nodeName.toLowerCase() === nodeName;

116                 };

117         },

118         "CLASS": function( className ) {

119             var className = className.toLowerCase();

120             return function( elem ) {

121                     return elem.className.toLowerCase() === className;

122             };

123         }

124     },

125     find: {

126         "TAG": function( tag, context ) {

127             return context.getElementsByTagName( tag );

128         },

129         "CLASS": support.getElementsByClassName&&function( tag, context ) {

130             return context.getElementsByClassName( tag );

131         },

132     },

133 }

134 // tokenize函数

135 // 将选择器字符串转化为方便使用的数组对象形式

136 tokenize = Sizzle.tokenize = function( selector, parseOnly ) {

137     var cached = tokenCache[ selector + " " ];

138     

139     // cached.slice生成新的数组,对其修改不会修改其引用缓存

140     if ( cached ) {

141         return cached.slice( 0 );

142     }

143     // 循环条件

144     var soFar = selector;

145     // 结果数组

146     var groups = [];

147     // 匹配参数

148     var matched;

149     // 一个独立的tokens

150     var tokens;

151     // 辅助变量

152     var match;

153 

154     while ( soFar ) {

155 

156         //首次默认创建一个tokens

157         //之后每碰到一个逗号新增一个新的tokens

158         if ( !matched || (match = rcomma.exec( soFar )) ) {

159             if ( match ) {

160                 // Don't consume trailing commas as valid

161                 soFar = soFar.slice( match[0].length ) || soFar;

162             }

163             groups.push( (tokens = []) );

164         }

165         

166         matched = false;

167 

168         // 关系token

169         if ( (match = rcombinators.exec( soFar )) ) {

170             matched = match.shift();

171             tokens.push({

172                 value: matched,

173                 // Cast descendant combinators to space

174                 type: match[0].replace( rtrim, " " )

175             });

176             soFar = soFar.slice( matched.length );

177         }

178 

179         // TAG,CLASS,ID token

180         for ( type in Expr.filter ) {

181             if ( match = matchExpr[ type ].exec( soFar ) ) {

182                 matched = match.shift();

183                 tokens.push({

184                     value: matched,

185                     type: type,

186                     matches: match

187                 });

188                 soFar = soFar.slice( matched.length );

189             }

190         }

191         // 一次循环到这里三个条件都不符合没有匹配结果时,跳出。

192         if ( !matched ) {

193             break;

194         }

195     }

196 

197     // 意外跳出,soFar存在,报错。

198     return soFar ?

199             Sizzle.error( selector ) :

200             // 缓存后转成新数组返回(预防修改缓存内容)

201             tokenCache( selector, groups ).slice( 0 );

202 };

203 // 将tokens转化为selector字符串形式。

204 function toSelector( tokens ) {

205     var i = 0,

206         len = tokens.length,

207         selector = "";

208     for ( ; i < len; i++ ) {

209         selector += tokens[i].value;

210     }

211     return selector;

212 }

213 // !addCombinator

214 // 增加关系处理函数

215 // 返回关系函数,主要功能是,遍历种子节点的关系节点。

216 // 比如li>a,传入无数个种子节点a,a.parentNode,再执行matcher,matcher里再判断这个父亲节点是不是li

217 function addCombinator( matcher, combinator ) {

218     var dir = combinator.dir;

219     return combinator.first ?

220         function( elem, context ) {

221             while( (elem = elem[ dir ]) ){

222                 if ( elem.nodeType === 1 ) {

223                     return matcher( elem, context );

224                 }

225             }

226         }:

227         function( elem, context ) {

228             while ( (elem = elem[ dir ]) ) {

229                 if ( elem.nodeType === 1 ) {

230                     if(matcher( elem, context )) {

231                         return true;

232                     }

233                 }

234             }

235             return false; 

236         }

237 }

238 

239 // !elementMatcher

240 // 生成matchers遍历器

241 // matchers数组存放我要过滤的函数,这个函数遍历所有过滤函数,一个不符合就返回false。

242 function elementMatcher( matchers ) {

243     return function( elem, context ) {

244         var i = matchers.length;

245         while ( i-- ) {

246             if ( !matchers[i]( elem, context ) ) {

247                 return false;

248             }

249         }

250         return true;

251     };

252 }

253 // !matcherFromTokens

254 // 根据tokens,生成过滤一组函数matchers,供elementMatcher使用

255 // 返回的是一个执行所有过滤函数的函数

256 function matcherFromTokens( tokens ){

257     var matchers = [];

258     var matcher;

259     var i = 0;

260     var len = tokens.length;

261     for ( ; i < len; i++ ) {

262         if ( (matcher = Expr.relative[ tokens[i].type ]) ) {

263             matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];

264         } else {

265             matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );

266             matchers.push( matcher );

267         }

268     }

269     return elementMatcher( matchers );

270 }

271 // !matcherFromGroupMatchers

272 // 返回超级匹配器,

273 function matcherFromGroupMatchers( elementMatchers ){

274     // !!最重要superMatcher,也是最核心的函数,其它的函数为它服务。

275     // 获取种子元素,遍历所有种子元素。

276     // 遍历elementMatchers

277     // 符合的推入结果数组

278     // 一个选择器(逗号隔开的)生成一个elementMatcher,elementMatchers是存放所有elementMatcher的数组

279     var superMatcher = function( seed, context, results) {

280         var elems = seed || Expr.find["TAG"]( "*", document );

281         var len = elems.length;

282         var i = 0;

283         for ( ; i !== len && (elem = elems[i]) != null; i++ ) {

284             j = 0;

285             while ( (matcher = elementMatchers[j++]) ) {

286                 if ( matcher( elem, context) ) {

287                     results.push( elem );

288                     break;

289                 }

290             }

291         }

292     }

293     return superMatcher;

294 }

295 // compile

296 // 最初的编译器,存放elementMatchers,缓存超级匹配函数并返回

297 compile = Sizzle.compile = function( selector, match ) {

298     var i;

299     var elementMatchers = [];

300     var    cached = compilerCache[ selector + " "];

301     if ( !cached ) {

302         i = match.length;

303         while ( i-- ) {

304             elementMatchers.push( matcherFromTokens( match[i] ) );

305         }

306         cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers ));

307     }

308     return cached;

309 }

310 // select

311 // 兼容的自写的选择器

312 select = Sizzle.select = function( selector, context, results){

313     var token;

314     var seed;

315     var tokens;

316     var find;

317     var match = tokenize( selector )

318     if ( match.length === 1 ) {

319         // tokens

320         var tokens = match[0].slice( 0 );

321         // 如果tokens的首项是ID,将其设置为上下文

322         if ( (token = tokens[0]).type === 'ID' ){    

323             context = document.getElementById(token.matches[0]);

324             selector = selector.slice( tokens.shift().value.length );

325         }

326         // 生成种子seed

327         // 如"div ul li",所谓种子就是所有的li

328         // 后面编译函数需要过滤出符合祖先是ul,ul的祖先是div的节点

329         i = tokens.length;

330         while ( i-- ){

331             token = tokens[i];

332             if ( Expr.relative[ (type = token.type) ] ) {

333                 break;

334             }

335             if((find =  Expr.find[ type ]))

336                 if( seed = find( token.matches[0],context ) ) {

337                     tokens.splice( i, 1 );

338                     selector = toSelector( tokens )

339                     break;

340                 }

341         } 

342     };

343     // 根据selector,match生成superMatcher并调用

344     compile( selector, match )( seed, context, results );

345     return results;

346 }

347 

348 // 对外入口

349 window.MSizzle = Sizzle;

350 

351 })(window)

352 // 测试

353 console.log(MSizzle("ul.topnav > li"))

354 console.log(MSizzle("ul.topnav   li"))

355 console.log(MSizzle("ul.topnav + div"))

356 console.log(MSizzle("ul.topnav ~ div"))

 1.先来整体流程,首先select生成种子seed,然后执行complie编译出超级匹配函数。在complie中,调用matchFromTokens生成每个tokens的匹配函数,如ul>li,div,会生成两个匹配函数,然后存入到elmentMatchers数组。然后,在matchFromTokens,根据tokens,返回匹配函数,elmentMatcher函数,和addCompinator是其辅助函数。然后回到complie中,缓存matcherFromGroupMatchers函数的结果并返回。在matcherFromGroupMatchers的返回superMatcher 函数中,遍历所有种子元素(不存在时的种子元素就是所有节点),利用elmentMatchers数组的匹配函数匹配。符合推入到结果数组中。跳回最初的complie返回的超级匹配函数,传入参数运行。

2.matchFromTokens如何工作。假如我们有tokens,[{type:'CLASS',value:"f"},{type:'CLASS',value:"box"}],seed是一群div节点,我们只要根据tokens生成两个函数,一个matchers数组中,一个函数是当当前节点的className是f时返回true,一个函数是当当前节点的className是box时返回true。外包一个函数elmentMatcher,参数是待匹配节点,执行数组matchers的所有的函数,但凡有一个不匹配直接返回false。

再假设tokens[{type:'TAG',value:'ul'},{type:'>',value:' > '}],seed是一群li节点。这种情况我们需要过滤li的父亲节点是否是ul。这个找到父亲节点的函数就是addCompinator。

3.matcherFromGroupMatchers工作。他是返回一个最终核心匹配函数,这个函数遍历所有节点,每个节点,执行返回elmentMatcher函数,如果返回为true,则存入到结果数组中。由于选择器一般也会包括几个独立的选择器,如ul,div,就是两个elmentMatcher函数存在elmentMatchers,所以还要遍历执行每一个elmentMatcher。

 

你可能感兴趣的:(实现)