zoukankan      html  css  js  c++  java
  • sizzle源码分析 (4)sizzle 技术总结及值得我们学习的地方

    分析sizzle源码并不是为了去钻牛角尖,而是去了解它的思想,学习下期中一些技术的运用。

    1,sizzle中的正则表达式
    jquery源码中充斥着各种正则表达式,能否看懂其源码的关键之一就是对正则表达式的理解。
    RegExp对象的exec方法:返回一个数组,第一个是匹配项,后边依次是分组匹配项,如果分组匹配不成功,则为undefined。而且还返回属性index(匹配项起始index),input(要验证的字符串)
    比如:

    var r=/^(d{1})s((w?)|(d{1}))$/.exec("2 ")
    r=["2 ", "2", "", "", undefined];
    r.index=0
    r.input="2 "


    了解了这个方法后,来具体分析下期中的正则表达式。

    匹配空白 水平制表符 回车 换行 换页

    whitespace = "[\x20\t\r\n\f]"


    说明:对于字符串需要双重转义。x20是空白符, 水平制表符, 回车, 换行,f换页

    characterEncoding = "(?:\\.|[\w-]|[^\x00-\xa0])+"


    说明:?: 非捕获性分组,不会创建反向引用的分组,即不会放在exec结果集中。\.匹配加任意字符;[w-]匹配[a-zA-Z0-9_]或-;[^\x00-\xa0]表示ISO 10646 characters 161 and higher

    attributes = "\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +"*(?:([*^$|!~]?=)" + whitespace + "*(?:(['"])((?:\\.|[^\\])*?)\3|(" + identifier + ")|)|)" + whitespace + "*\]"


    说明:3反向引用第三个匹配项。此正则用来匹配属性 例如:[a="b"] [a=b] [a|='er']

    rtrim=/^[x20	
    f]+|((?:^|[^\])(?:\.)*)[x20	
    f]+$/g


    说明:匹配首位字符串

    rcomma=/^[x20	
    f]*,[x20	
    f]*/


    说明:匹配是否为逗号分隔

    rcombinators=/^[x20	
    f]*([>+~]|[x20	
    f])[x20	
    f]*/


    说明:匹配是否是关系符

    rsibling=/[x20	
    f]*[+~]/


    说明:兄弟节点标示符

    rattributeQuotes=/=[x20	
    f]*([^]'"]*)[x20	
    f]*]/g


    说明:属性的value

    rpseudo=/:((?:\.|[w-]|[^x00-xa0])+)(?:(((['"])((?:\.|[^\])*?)3|((?:\.|[^\()[]]|[[x20	
    f]*((?:\.|[w-]|[^x00-xa0])+)[x20	
    f]*(?:([*^$|!~]?=)[x20	
    f]*(?:(['"])((?:\.|[^\])*?)8|((?:\.|[w#-]|[^x00-xa0])+)|)|)[x20	
    f]*])*)|.*))|)/


    说明:伪类选择器

    matchExpr={
    ATTR: /^[[x20	
    f]*((?:\.|[w-]|[^x00-xa0])+)[x20	
    f]*(?:([*^$|!~]?=)[x20	
    f]*(?:(['"])((?:\.|[^\])*?)3|((?:\.|[w#-]|[^x00-xa0])+)|)|)[x20	
    f]*]/,
    
    CHILD: /^:(only|first|last|nth|nth-last)-(child|of-type)(?:([x20	
    f]*(even|odd|(([+-]|)(d*)n|)[x20	
    f]*(?:([+-]|)[x20	
    f]*(d+)|))[x20	
    f]*)|)/i,
    
    CLASS: /^.((?:\.|[w-]|[^x00-xa0])+)/,
    
    ID: /^#((?:\.|[w-]|[^x00-xa0])+)/,
    
    PSEUDO: /^:((?:\.|[w-]|[^x00-xa0])+)(?:(((['"])((?:\.|[^\])*?)3|((?:\.|[^\()[]]|[[x20	
    f]*((?:\.|[w-]|[^x00-xa0])+)[x20	
    f]*(?:([*^$|!~]?=)[x20	
    f]*(?:(['"])((?:\.|[^\])*?)8|((?:\.|[w#-]|[^x00-xa0])+)|)|)[x20	
    f]*])*)|.*))|)/,
    
    TAG: /^((?:\.|[w*-]|[^x00-xa0])+)/,
    
    bool: /^(?:checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$/i,
    
    needsContext: /^[x20	
    f]*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:([x20	
    f]*((?:-d)?d*)[x20	
    f]*)|)(?=[^-]|$)/i
    }

    说明:matchExpr是各种正则的集合对象

    rnative=/^[^{]+{s*[native w/


    说明:用来判断是否是浏览器本地对象方法或属性

    runescape=/\([da-f]{1,6}[x20	
    f]?|([x20	
    f])|.)/gi


    说明:匹配16进制字符

    funescape = function( _, escaped, escapedWhitespace ) {//_代表整个匹配项,escaped代表第一个匹配项,escapedWhitespace代表第二个分组匹配项,即空白匹配项
    var high = "0x" + escaped - 0x10000;
    // NaN means non-codepoint
    // Support: Firefox
    // Workaround erroneous numeric interpretation of +"0x"
    return high !== high || escapedWhitespace ?
    escaped :
    // BMP codepoint
    high < 0 ?
    String.fromCharCode( high + 0x10000 ) :
    // Supplemental Plane codepoint (surrogate pair)
    String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
    };


    说明:解码函数,与runescape联合使用


    2,sizzle中的几个函数也值得我们学习,它们给我们了一种运用思路

    function assert( fn ) {
        var div = document.createElement("div");
    
        try {
            return !!fn( div );
        } catch (e) {
            return false;
        } finally {
            // Remove from its parent by default
            if ( div.parentNode ) {
                div.parentNode.removeChild( div );
            }
            // release memory in IE
            div = null;
        }
    }

    说明:断言函数,用来判断某些条件是否成立,比如有没有某个属性,方法,比如:

    support.attributes = assert(function( div ) {
            div.className = "i";
            return !div.getAttribute("className");
        });

    用来判断是否支持getAttribute方法。

    再看elementMatcher函数:

    //将之前的匹配函数综合成一个匹配函数
    function elementMatcher( matchers ) {
        return matchers.length > 1 ?
            function( elem, context, xml ) {
                var i = matchers.length;
                while ( i-- ) {
                    if ( !matchers[i]( elem, context, xml ) ) {
                        return false;
                    }
                }
                return true;
            } :
            matchers[0];
    }

    matchers是一个函数数组,此方法可以把数组的多个函数合并成一个函数。

    再来看看matcherFromGroupMatchers函数:

    function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
        var matcherCachedRuns = 0,
            bySet = setMatchers.length > 0,
            byElement = elementMatchers.length > 0,
            //最终运行的匹配函数:
            superMatcher = function( seed, context, xml, results, expandContext ) {
                var elem, j, matcher,
                    setMatched = [],
                    matchedCount = 0,
                    i = "0",
                    unmatched = seed && [],
                    outermost = expandContext != null,
                    contextBackup = outermostContext,
                    // We must always have either seed elements or context
                    elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
                    // Use integer dirruns iff this is the outermost matcher
                    dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);
    
                if ( outermost ) {
                    outermostContext = context !== document && context;
                    cachedruns = matcherCachedRuns;
                }
    
                // Add elements passing elementMatchers directly to results
                // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
                for ( ; (elem = elems[i]) != null; i++ ) {
                    if ( byElement && elem ) {
                        j = 0;
                        while ( (matcher = elementMatchers[j++]) ) {
                            if ( matcher( elem, context, xml ) ) {
                                results.push( elem );
                                break;
                            }
                        }
                        if ( outermost ) {
                            dirruns = dirrunsUnique;
                            cachedruns = ++matcherCachedRuns;
                        }
                    }
    
                    // Track unmatched elements for set filters
                    if ( bySet ) {
                        // They will have gone through all possible matchers
                        if ( (elem = !matcher && elem) ) {
                            matchedCount--;
                        }
    
                        // Lengthen the array for every element, matched or not
                        if ( seed ) {
                            unmatched.push( elem );
                        }
                    }
                }
    
                // Apply set filters to unmatched elements
                matchedCount += i;
                if ( bySet && i !== matchedCount ) {
                    j = 0;
                    while ( (matcher = setMatchers[j++]) ) {
                        matcher( unmatched, setMatched, context, xml );//这里开始调用matcher 即:
                    }
    
                    if ( seed ) {
                        // Reintegrate element matches to eliminate the need for sorting
                        if ( matchedCount > 0 ) {
                            while ( i-- ) {
                                if ( !(unmatched[i] || setMatched[i]) ) {
                                    setMatched[i] = pop.call( results );
                                }
                            }
                        }
    
                        // Discard index placeholder values to get only actual matches
                        setMatched = condense( setMatched );
                    }
    
                    // Add matches to results
                    push.apply( results, setMatched );
    
                    // Seedless set matches succeeding multiple successful matchers stipulate sorting
                    if ( outermost && !seed && setMatched.length > 0 &&
                        ( matchedCount + setMatchers.length ) > 1 ) {
    
                        Sizzle.uniqueSort( results );
                    }
                }
    
                // Override manipulation of globals by nested matchers
                if ( outermost ) {
                    dirruns = dirrunsUnique;
                    outermostContext = contextBackup;
                }
    
                return unmatched;
            };
    
        return bySet ?
            markFunction( superMatcher ) :
            superMatcher;
    }

     此函数通过闭包原理,返回终极匹配器superMatcher

    在sizzle中,闭包的运用还有setMatcher

    //第1个参数,preFilter,前置过滤器,相当于“div”过滤器
    //第2个参数,selector,前置过滤器的字符串格式,相当于“div”input:checked + p
    //第3个参数,matcher,当前位置伪类“:first”的匹配器/过滤器
    //第4个参数,postFilter,后置过滤器,相当于“ ”
    //第5个参数,postFinder,后置搜索器,相当于在前边过滤出来的集合里边再搜索剩下的规则的一个搜索器
    //第6个参数,postSelector,后置搜索器对应的选择器字符串,相当于“input:checked + p”
    //伪类选择器时会执行这个函数
    function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
        if ( postFilter && !postFilter[ expando ] ) {
            postFilter = setMatcher( postFilter );
        }
        if ( postFinder && !postFinder[ expando ] ) {
            postFinder = setMatcher( postFinder, postSelector );
        }
        return markFunction(function( seed, results, context, xml ) {
            var temp, i, elem,
                preMap = [],
                postMap = [],
                preexisting = results.length,
    
                // Get initial elements from seed or context
                /*
                如果执行$("ul.list>li span:eq(1)")时:则会调用multipleContexts来递归sizzle
                递归后就得到elems为两个span啦
                */
                elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
    
                // Prefilter to get matcher input, preserving a map for seed-results synchronization
                /*这里matcherIn就是两个span啦*/
                matcherIn = preFilter && ( seed || !selector ) ?
                    condense( elems, preMap, preFilter, context, xml ) :
                    elems,
    
                matcherOut = matcher ?
                    // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
                    postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
    
                        // ...intermediate processing is necessary
                        [] :
    
                        // ...otherwise use results directly
                        results :
                    matcherIn;
    
            // Find primary matches
            if ( matcher ) {
                matcher( matcherIn, matcherOut, context, xml );
            }
    
            // Apply postFilter
            if ( postFilter ) {
                temp = condense( matcherOut, postMap );
                postFilter( temp, [], context, xml );
    
                // Un-match failing elements by moving them back to matcherIn
                i = temp.length;
                while ( i-- ) {
                    if ( (elem = temp[i]) ) {
                        matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
                    }
                }
            }
    
            if ( seed ) {
                if ( postFinder || preFilter ) {
                    if ( postFinder ) {
                        // Get the final matcherOut by condensing this intermediate into postFinder contexts
                        temp = [];
                        i = matcherOut.length;
                        while ( i-- ) {
                            if ( (elem = matcherOut[i]) ) {
                                // Restore matcherIn since elem is not yet a final match
                                temp.push( (matcherIn[i] = elem) );
                            }
                        }
                        postFinder( null, (matcherOut = []), temp, xml );
                    }
    
                    // Move matched elements from seed to results to keep them synchronized
                    i = matcherOut.length;
                    while ( i-- ) {
                        if ( (elem = matcherOut[i]) &&
                            (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
    
                            seed[temp] = !(results[temp] = elem);
                        }
                    }
                }
    
            // Add elements to results, through postFinder if defined
            } else {
                matcherOut = condense(
                    matcherOut === results ?
                        matcherOut.splice( preexisting, matcherOut.length ) :
                        matcherOut
                );
                if ( postFinder ) {
                    postFinder( null, results, matcherOut, xml );
                } else {
                    push.apply( results, matcherOut );
                }
            }
        });
    }

    这样做的好处是保证了每个匹配词的处理函数的当前执行上下文,以便后边调用。

  • 相关阅读:
    Data Guard相关参数学习介绍
    Android打包失败Proguard returned with error code 1. See console
    Extjs4 类的定义和扩展
    c#中常用的异常类型
    cocos2d-x过程动作CCProgressTo示例学习笔记
    欧拉函数
    sae上屏蔽错误显示并查看错误日志
    在wdcp环境下架设VSFTPD虚拟用户只上传功能服务器
    Android开发匹配字符笔记
    setImageResource与setImageBitmap的区别
  • 原文地址:https://www.cnblogs.com/mufc-go/p/3324488.html
Copyright © 2011-2022 走看看