• Sizzle一步步实现所有功能(基本筛选)


    第二步:实现:first,:last,:eq(),even,odd,:gt(),:lt(); :header,:root,:taget; :not()。

    ;(function( window ){
    var arr = [];
    var push = arr.push;
    var slice = arr.slice;
    var select ;
    var Expr;
    // 标识
    var expando = "sizzle" + 1 * new Date();
    // http://www.w3.org/TR/css3-selectors/#whitespace
    // 各种空白待穿正则字符串
    var whitespace = "[\x20\t\r\n\f]";
    // 带空格选择器正则,记忆无空格选择器
    // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
    var    identifier = "(?:\\.|[\w-]|[^\x00-\xa0])+";
    var pseudos = ":(" + identifier + ")(?:\((" +
            // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
            // 1. quoted (capture 3; capture 4 or capture 5)
            "('((?:\\.|[^\\'])*)'|"((?:\\.|[^\\"])*)")|" +
            // 2. simple (capture 6)
            "((?:\\.|[^\\()[\]]|" + attributes + ")*)|" +
            // 3. anything else (capture 2)
            ".*" +
            ")\)|)";
    // 属性选择器: http://www.w3.org/TR/selectors/#attribute-selectors
    var    attributes = "\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
            // Operator (capture 2)
            "*([*^$|!~]?=)" + whitespace +
            // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
            "*(?:'((?:\\.|[^\\'])*)'|"((?:\\.|[^\\"])*)"|(" + identifier + "))|)" + whitespace +
            "*\]";
    var rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\])(?:\\.)*)" + whitespace + "+$", "g" );
    // 快速选择器正则 ID 或者 TAG(包括*) 或者 CLASS 选择器
    var rquickExpr = /^(?:#([w-]+)|(w+|*)|.([w-]+))$/;
    // 连接符号
    var rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" );
    // 层级符号正则'>',' ','+','~'
    var rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" );
    var matchExpr = {
            "ID": new RegExp( "^#(" + identifier + ")" ),
            "CLASS": new RegExp( "^\.(" + identifier + ")" ),
            "TAG": new RegExp( "^(" + identifier + "|[*])" ),
            "PSEUDO": new RegExp( "^" + pseudos ),
            "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\(" +
                whitespace + "*((?:-\d)?\d*)" + whitespace + "*\)|)(?=[^-]|$)", "i" )
    };
    var rheader = /^hd$/i;
    // 浏览器代码正则
    var rnative = /^[^{]+{s*[native w/;
    // token缓存
    var tokenCache = createCache();
    // 编译缓存
    var compilerCache = createCache();
    // 入口
    function Sizzle( selector, context, results){
        // 清除空格
        selector = selector.replace( rtrim, "$1" )
        var results = results || [];
        var match;
        var matcher;
        var elem;
        var m;
        var context = context || document;
        
        // 是否为最简选择器
        if( match = rquickExpr.exec( selector )){
            // Sizzle('#ID)
            if ( (m = match[1]) ) {
                elem = context.getElementById( m );
                if( elem ){
                    results.push( elem );
                }
                return results;
                
            // Sizzle("TAG")
            }else if( (m = match[2]) ){
                push.apply( results, context.getElementsByTagName( selector ) );
                return results;
            
            // Sizzle(".CLASS")    
            }else if( (m = match[3]) ){
                // 支持getElementsByClassName
                if( support.getElementsByClassName ){
                    push.apply( results, context.getElementsByClassName( m ) );
                    return results;
                }
            }
        }
        // 复杂选择调到select
        return select( selector, context, results);
    }
    // 创建缓存函数
    function createCache() {
        var keys = [];
    
        function cache( key, value ) {
            // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
            if ( keys.push( key + " " ) > 10 ) {
                // Only keep the most recent entries
                delete cache[ keys.shift() ];
            }
            return (cache[ key + " " ] = value);
        }
        return cache;
    }
    // 函数标记函数
    function markFunction( fn ) {
        fn[ expando ] = true;
        return fn;
    }
    // 错误函数
    Sizzle.error = function( msg ) {
        throw new Error( "Syntax error, unrecognized expression: " + msg );
    };
    // 版本支持变量的对外访问入口
    var support = Sizzle.support = {};
    
    // 判断是否支持getElementsByClassName
    // 支持: IE<9
    support.getElementsByClassName = rnative.test( document.getElementsByClassName );
    // 表达式对象
    // 存放各类相对位置,各种查询函数,各种过滤函数等。
    Expr = {
        relative: {
            ">": { dir: "parentNode", first: true },
            " ": { dir: "parentNode" },
            "+": { dir: "previousSibling", first: true },
            "~": { dir: "previousSibling" }
        },
        filter: {
            "TAG": function( nodeNameSelector ) {
                var nodeName = nodeNameSelector.toLowerCase();
                return nodeNameSelector === "*" ?
                    function() { return true; } :
                    function( elem ) {
                        return elem.nodeName.toLowerCase() === nodeName;
                    };
            },
            "CLASS": function( className ) {
                var className = className.toLowerCase();
                return function( elem ) {
                        return elem.className.toLowerCase() === className;
                };
            },
            "PSEUDO": function( pseudo, argument ) {
                var fn = Expr.pseudos[ pseudo ];
                if ( fn[ expando ] ) {
                    return fn( argument );
                }
                return fn;
            }
        },
        find: {
            "TAG": function( tag, context ) {
                return context.getElementsByTagName( tag );
            },
            "CLASS": support.getElementsByClassName&&function( tag, context ) {
                return context.getElementsByClassName( tag );
            },
        },
        // 筛选方法
        pseudos: {
            // not是个难点
            // superMatcher将结果存入restlts,未匹配的元素则返回
            // not两个路线一个是处理not参数中为复杂伪类筛选setMatcher
            // 一个是elementMatcher
            "not": markFunction(function( selector ){
                    var results = [];
                    var input = [];
                    var matcher = compile( selector );
                    return matcher[ expando ] ?  
                    markFunction(function( seed, matches, context){
                        var elem;
                        var unmatched = matcher(seed, null, [] );
                        var i = seed.length;
                        while ( i-- ) {
                            if ( (elem = unmatched[i]) ) {
                                seed[i] = !(matches[i] = elem);
                            }
                        }
                    }):
                    function( elem, context ) {
                        input[0] = elem;
                        matcher( input, null, results );
                        return !results.pop();
                    }
                }
            ),
            "target": function( elem ) {
                var hash = window.location && window.location.hash;
                return hash && hash.slice( 1 ) === elem.id;
            },
            "header": function( elem ) {
                return rheader.test( elem.nodeName );
            },
            "root": function( elem ) {
                return elem === document.documentElement;
            },
            // 以下为位置筛选,我们采用一个函数,但其index不同。
            // 用createPositionalPseudo生成一个新函数,这个函数的index数组根据位置不同而不同
            "first": createPositionalPseudo(
                function(){
                    return [0];
                }
            ),
            "last":createPositionalPseudo(
                function( matchesIndex ,length){
                    return [ length-1 ];
                }
            ),
            "eq": createPositionalPseudo(
                function( matchesIndex ,length, argument){
                    return [ argument < 0 ? argument + length : argument ];
                }
            ),
            "even": createPositionalPseudo(
                function( matchesIndex, length ){
                    for(var i = 0; i<length; i+=2){
                        matchesIndex.push(i);
                    }
                    return matchesIndex;
                }
            ),
            "odd": createPositionalPseudo(
                function( matchesIndex, length ){
                    for(var i = 1; i<length; i+=2){
                        matchesIndex.push(i);
                    }
                    return matchesIndex;
                }
            ),
            "lt": createPositionalPseudo(
                function( matchesIndex ,length, argument){
                    var i = argument < 0 ? argument + length : argument;
                    while(i--){
                        matchesIndex.push(i);
                    }
                    return matchesIndex;
                }
            ),
            "gt": createPositionalPseudo(
                function( matchesIndex, length, argument){
                    var i = argument < 0 ? argument + length : argument;
                    while( ++i < length){
                        matchesIndex.push(i);
                    }
                    return matchesIndex;
                }
            ),
        }
    }
    // 返回一个函数,用于所有位置筛选
    function createPositionalPseudo( fn ) {
        return markFunction( function( argument ){
            argument = + argument;
            return markFunction(function( seed, matches ) {
                var j;
                var matchIndexes = fn([],seed.length,argument);
                var i = matchIndexes.length;
                while(i--){
                    if ( seed[ (j = matchIndexes[i]) ] ) {
                        seed[j] = !(matches[j] = seed[j]);
                    }
                }
            })
        })
    }
    // tokenize函数
    // 将选择器字符串转化为方便使用的数组对象形式
    tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
        var cached = tokenCache[ selector + " " ];
        
        // cached.slice生成新的数组,对其修改不会修改其引用缓存
        if ( cached ) {
            return cached.slice( 0 );
        }
        // 循环条件
        var soFar = selector;
        // 结果数组
        var groups = [];
        // 匹配参数
        var matched;
        // 一个独立的tokens
        var tokens;
        // 辅助变量
        var match;
    
        while ( soFar ) {
    
            //首次默认创建一个tokens
            //之后每碰到一个逗号新增一个新的tokens
            if ( !matched || (match = rcomma.exec( soFar )) ) {
                if ( match ) {
                    // Don't consume trailing commas as valid
                    soFar = soFar.slice( match[0].length ) || soFar;
                }
                groups.push( (tokens = []) );
            }
            
            matched = false;
    
            // 关系token
            if ( (match = rcombinators.exec( soFar )) ) {
                matched = match.shift();
                tokens.push({
                    value: matched,
                    // Cast descendant combinators to space
                    type: match[0].replace( rtrim, " " )
                });
                soFar = soFar.slice( matched.length );
            }
            // TAG,CLASS,ID token
            for ( type in Expr.filter ) {
                if ((match = matchExpr[ type ].exec( soFar )) ) {
                    matched = match.shift();
                    tokens.push({
                        value: matched,
                        type: type,
                        matches: match
                    });
                    soFar = soFar.slice( matched.length );
                }
            }
            // 一次循环到这里三个条件都不符合没有匹配结果时,跳出。
            if ( !matched ) {
                break;
            }
        }
        // 意外跳出,soFar存在,报错。
        return soFar ?
                Sizzle.error( selector ) :
                // 缓存后转成新数组返回(预防修改缓存内容)
                tokenCache( selector, groups ).slice( 0 );
    };
    // 将tokens转化为selector字符串形式。
    function toSelector( tokens ) {
        var i = 0,
            len = tokens.length,
            selector = "";
        for ( ; i < len; i++ ) {
            selector += tokens[i].value;
        }
        return selector;
    }
    // 压缩数组
    // condense([undefined,1,2,3]) = [123];
    function condense(matcherOut) {
        var newMatcherOut = [];
        var elem;
        for(var i =0; i< matcherOut.length;i++){
            if((elem = matcherOut[i])){
                newMatcherOut.push(elem);
            }
        }
        return newMatcherOut;
    }
    // 对多重上下文执行元素选择
    // 用于上下文是数组的情况
    // 服务于setMatcher返回函数的某些情况
    function multipleContexts(selector,context,results){
        for(var i = 0;i<context.length;i++){
            Sizzle(selector, context[i],results);
        }
        return results;
    }
    // setMatcher筛选
    // setMatcher是不同于elementMatcher的另一个方向,用来处理各种伪类筛选
    function setMatcher( preFilter, selector, matcher, postFinder, postSelector){
        if ( postFinder && !postFinder[ expando ] ) {
            postFinder = setMatcher( postFinder, postSelector );
        }
        return markFunction(function( seed, results, context ) {
            var elems =  seed ||  multipleContexts( selector || "*" , context.nodeType ? [ context ] : context ,[]);
            var matcherIn = elems;
            var matcherOut = [];
            if ( matcher ) {
                matcher( matcherIn, matcherOut);
            }
            matcherOut = condense(matcherOut);
            if ( postFinder ) {
                postFinder( null, results, matcherOut );
            } else {
                push.apply( results, matcherOut );
            }
        })
    }
    // !addCombinator
    // 增加关系处理函数
    // 返回关系函数,主要功能是,遍历种子节点的关系节点。
    // 比如li>a,传入无数个种子节点a,a.parentNode,再执行matcher,matcher里再判断这个父亲节点是不是li
    function addCombinator( matcher, combinator ) {
        var dir = combinator.dir;
        return combinator.first ?
            function( elem, context ) {
                while( (elem = elem[ dir ]) ){
                    if ( elem.nodeType === 1 ) {
                        return matcher( elem, context );
                    }
                }
            }:
            function( elem, context ) {
                while ( (elem = elem[ dir ]) ) {
                    if ( elem.nodeType === 1 ) {
                        if(matcher( elem, context )) {
                            return true;
                        }
                    }
                }
                return false; 
            }
    }
    
    // !elementMatcher
    // 生成matchers遍历器
    // matchers数组存放我要过滤的函数,这个函数遍历所有过滤函数,一个不符合就返回false。
    function elementMatcher( matchers ) {
        return function( elem, context ) {
            var i = matchers.length;
            while ( i-- ) {
                if ( !matchers[i]( elem, context ) ) {
                    return false;
                }
            }
            return true;
        };
    }
    // !matcherFromTokens
    // 根据tokens,生成过滤一组函数matchers,供elementMatcher使用
    // 返回的是一个执行所有过滤函数的函数
    function matcherFromTokens( tokens ){
                
        var matchers = [];
        var matcher;
        var type;
        var i = 0;
        var j ;
        var len = tokens.length;
        for ( ; i < len; i++ ) {
            if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
                matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
            } else {
                matcher = Expr.filter[ (type = tokens[i].type) ].apply( null, tokens[i].matches );
                if ( matcher[ expando ] ) {
                    j = i+1;
                    return setMatcher(
                        i > 0 && elementMatcher( matchers ),
                        i > 0 && toSelector( tokens.slice( 0, i ) ),
                        matcher,
                        j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
                        j < len && toSelector( tokens )
                    );
                }
                matchers.push( matcher );
            }
        }
        return elementMatcher( matchers );
    }
    // !matcherFromGroupMatchers
    // 返回超级匹配器,
    function matcherFromGroupMatchers( elementMatchers, setMatchers ){
        var bySet = setMatchers.length > 0,
            byElement = elementMatchers.length > 0;
        // !!最重要superMatcher,也是最核心的函数,其它的函数为它服务。
        // 获取种子元素,遍历所有种子元素。
        // 遍历elementMatchers
        // 符合的推入结果数组
        // 一个选择器(逗号隔开的)生成一个elementMatcher,elementMatchers是存放所有elementMatcher的数组
        var superMatcher = function( seed, context, results) {
            var elems = seed || Expr.find["TAG"]( "*", document );
            var len = elems.length
            var i = 0;
            var j;
            var unmatched = seed ? slice.call(seed, 0) : undefined ;
            var setMatched = [];
            for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
                if( byElement ){
                    j = 0;
                    while ( (matcher = elementMatchers[j++]) ) {
                        if ( matcher( elem, context) ) {
                            results.push( elem );
                            break;
                        }
                    }
                    
                }
            }
            if ( bySet) {
                j = 0;
                while ( (matcher = setMatchers[j++]) ) {
                    matcher( unmatched, setMatched, context);
                }
                push.apply( results, setMatched );
            }
            return unmatched;
        }
        return bySet ?
            markFunction( superMatcher ) :
            superMatcher;;
    }
    // compile
    // 最初的编译器,存放elementMatchers,缓存超级匹配函数并返回
    compile = Sizzle.compile = function( selector, match ) {
        var i;
        var elementMatchers = [];
        var setMatchers = [];
        var    cached = compilerCache[ selector + " "];    
        if ( !cached ) {
            if ( !match ) {
                match = tokenize( selector );
            }
            i = match.length;
            while ( i-- ) {
                cached = matcherFromTokens( match[i] );
                if ( cached[ expando ] ) {
                    setMatchers.push( cached );
                } else {
                    elementMatchers.push( cached );
                }
            }
            cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ));
        }
        return cached;
    }
    // select
    // 兼容的自写的选择器
    select = Sizzle.select = function( selector, context, results){
        var token;
        var seed;
        var tokens;
        var find;
        var match = tokenize( selector )
        if ( match.length === 1 ) {
            // tokens
            var tokens = match[0].slice( 0 );
            // 如果tokens的首项是ID,将其设置为上下文
            if ( (token = tokens[0]).type === 'ID' ){    
                context = document.getElementById(token.matches[0]);
                selector = selector.slice( tokens.shift().value.length );
            }
            // 生成种子seed
            // 如"div ul li",所谓种子就是所有的li
            // 后面编译函数需要过滤出符合祖先是ul,ul的祖先是div的节点
            // 在筛选选择中,如li:eq(0),不可以从左边直接取种子。
            i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;;
            while ( i-- ){
                token = tokens[i];
                if ( Expr.relative[ (type = token.type) ] ) {
                    break;
                }
                if((find =  Expr.find[ type ]))
                    if( seed = find( token.matches[0],context ) ) {
                        tokens.splice( i, 1 );
                        selector = toSelector( tokens )
                        break;
                    }
            } 
        };
        // 根据selector,tokens(match防止对原tokens修改)生成superMatcher并调用
        compile( selector, match )( seed, context, results );
        return results;
    }
    
    // 对外入口
    window.MSizzle = Sizzle;
    
    })(window)
    // 测试/*
    console.log(MSizzle("a:not(.b)"))
    console.log(Sizzle("a:not(.b)"))

    1.整体的思路是,comple生成超级选择函数,这个函数主要用两种情况,一种是以前的elementMatcher,一种是setMatcher,用函数expando属性来区别执行哪条。本章的内容主要分析伪类筛选的基本筛选,大多数都是setMatcher路线,当然少数如:header,:root,:target则不是。

    我们继续以一条路径来分析setMatcher路线,div:first,在setMatcher函数里,先根据前面的选择器执行Sizzle(’div‘),在这些div里进行基本筛选的first筛选。

    2.markFunction,对函数进行标记并返回函数。Sizzle中根据选择器获取函数,如果有标记属性,则存入setMatchers。这是伪类路线,否则存入elementMatchers,一般路线。示例:

     1 var expando = "sizzle" + 1 * new Date();
     2 var filter = {
     3     'pseudos': markFunction(function(){
     4     
     5     }),
     6     'TAG': function(){
     7     
     8     }
     9 }
    10 function markFunction(fn){
    11     fn[expando] = true;
    12     return fn;
    13 }
    14 function devider(selectorType){
    15     if( filter[type][expando] ){
    16         console.log('伪类路线')
    17     }else{
    18         console.log('基本路线')
    19     }
    20 }
    21 devider('pseudos')
    View Code

    3.只要选择器中有走setMatchers路线的方法

  • 相关阅读:
    asp.net笔记第一章
    数据库复习笔记
    tp5博客项目实战2
    springboot调整MybatisPlus全局的验证策略
    SpringBoot整合MybatisPlus,并实现新增、修改、删除、查看、分页
    springboot整合Apollo
    创建apollo项目,并发布配置
    Apollo部门管理
    搭建Apollo环境(Ubuntu-18.04.4)
    启动apollo时出现的问题,../demo.sh: 行 84: curl: 未找到命令
  • 原文地址:https://www.cnblogs.com/winderby/p/4212874.html
Copyright © 2020-2023  润新知