klvoek

klvoek

(七)jQuery.extend代码段

就过之前对grep、map、merge的准备,现在可以入手flter函数了:

filter: function(t,r,not) {
    // Figure out if we're doing regular, or inverse, filtering     var g = not !== false ? jQuery.grep :
        function(a,f) {return jQuery.grep(a,f,true);};
     
    while ( t && /^[a-z[({<*:.#]/i.test(t) ) {
        var p = jQuery.parse;
        for ( var i = 0; i < p.length; i++ ) {
            var re = new RegExp( "^" + p[i][0]
                // Look for a string-like sequence                 .replace( 'S', "([a-z*_-][a-z0-9_-]*)" )
                // Look for something (optionally) enclosed with quotes                 .replace( 'Q', " *'?\"?([^'\"]*?)'?\"? *" ), "i" );
            var m = re.exec( t );
            if ( m ) {
                // Re-organize the match                 if ( p[i][1] )
                    m = ["", m[1], m[3], m[2], m[4]];
                // Remove what we just matched                 t = t.replace( re, "" );
                break;
            }
        }
        // :not() is a special case that can be optomized by         // keeping it out of the expression list         if ( m[1] == ":" && m[2] == "not" )
            r = jQuery.filter(m[3],r,false).r;
         
        // Otherwise, find the expression to execute         else {
            var f = jQuery.expr[m[1]];
            if ( f.constructor != String )
                f = jQuery.expr[m[1]][m[2]];
                 
            // Build a custom macro to enclose it             eval("f = function(a,i){" +
                ( m[1] == "@" ? "z=jQuery.attr(a,m[3]);" : "" ) +
                "return " + f + "}");
             
            // Execute it against the current filter             r = g( r, f );
        }
    }
    // Return an array of filtered elements (r)     // and the modified expression string (t)     return { r: r, t: t };
}

filter函数可以分为三段来看待

  • 获取正确正则处理函数g(来自于grep,主要是对not选择器进行的调整)

循环开始<直到所有的过滤条件处理完>

  • 从给定过滤条件字符串中取出一个过滤条件
  • 根据过滤条件获取对应的正则表达式,并通过g函数对目标集合进行过滤

循环结束

 

获取正确的正则处理函数g,一般的grep是找出符合条件的结果,当在not筛选时要翻转为找出不符合条件的结果。

// Figure out if we're doing regular, or inverse, filtering var g = not !== false ? jQuery.grep :
    function(a,f) {return jQuery.grep(a,f,true);};

循环开始

while ( t && /^[a-z[({<*:.#]/i.test(t) ) {
     ...
}

这里的正则表达式,负责检查字符串 t 的开头是不是符合要求的字符。

从给定的过滤条件字符串中取出一个过滤条件

var p = jQuery.parse;for ( var i = 0; i < p.length; i++ ) {
    var re = new RegExp( "^" + p[i][0]
        // Look for a string-like sequence         .replace( 'S', "([a-z*_-][a-z0-9_-]*)" )
        // Look for something (optionally) enclosed with quotes         .replace( 'Q', " *'?\"?([^'\"]*?)'?\"? *" ), "i" );
    var m = re.exec( t );
    if ( m ) {
        // Re-organize the match         if ( p[i][1] )
            m = ["", m[1], m[3], m[2], m[4]];
        // Remove what we just matched         t = t.replace( re, "" );
        break;
    }
}

这段代码里引用了jQuery.parse属性,我们看一下parse属性的定义代码:

// The regular expressions that power the parsing engine parse: [
    // Match: [@value='test'], [@foo]     [ "\\[ *(@)S *([!*$^=]*) *Q\\]", 1 ],
    // Match: [div], [div p]     [ "(\\[)Q\\]", 0 ],
    // Match: :contains('foo')     [ "(:)S\\(Q\\)", 0 ],
    // Match: :even, :last-chlid     [ "([:.#]*)S", 0 ]
]

这两段代码逻辑并不复杂,正则表达式不怎么好的同学一定要沉住气(^_^我看到正则表达式一对一对的特殊字符也眼晕,坚持住就习惯了)。首先,在第一眼看到parse属性的定义时会觉得很莫名奇妙。如果尝试从正则表达式的角度去对里面的内容进行分析的话,你将会被大Q和大S搞的晕头晕脑。然而,如果将parse属性结合最上面filter中的那段代码来分析的话,很容易就找到重点。从最粗略的代码结构看其,filter中这段代码是一个对parse数组的for循环,在循环体对var re赋值的代码我们很快就找到了大Q和大S的奥秘所在。原来,parse中掺杂了作者自己的伪代码思想,即在这一出的for循环中把parse中的伪代码替换并获得最终的正则表达式。接下来的一行代码就是使用正则表达式对目标字符串进行匹配。剩下的代码则是在匹配成功后,对匹配结果和原字符串的处理。对匹配结果进行的处理是按照parse中设置的标记对结果进行重新排序,综合代码可知只有parse中第一行的正则表达式匹配成功的结果需要重新排序。对原字符串的处理则是在匹配成功后一定会执行的处理过程,即将已经提取出的过滤条件从原字符串中移除。最后一句break的作用是,取出一个过滤条件后就跳出分析过滤条件的循环,进入过滤目标集合的阶段。

根据过滤条件获取对应的正则表达式,并通过g函数对目标集合进行过滤

// :not() is a special case that can be optomized by // keeping it out of the expression list if ( m[1] == ":" && m[2] == "not" )
    r = jQuery.filter(m[3],r,false).r;// Otherwise, find the expression to execute else {
    var f = jQuery.expr[m[1]];
    if ( f.constructor != String )
        f = jQuery.expr[m[1]][m[2]];
         
    // Build a custom macro to enclose it     eval("f = function(a,i){" +
        ( m[1] == "@" ? "z=jQuery.attr(a,m[3]);" : "" ) + 
        "return " + f + "}");
     
    // Execute it against the current filter     r = g( r, f );
}

这个阶段的第一行代码,是对not过滤条件的特殊处理(相当于,正常情况下filter筛选符合条件的结果,而当遇到not过滤条件是,则发生翻转,筛选出不符合条件的结果。条件是指:not(条件))。接下来的重点,就是对于jQuery.expr的构造了。这个构造思路在我们理解后显得不是很复杂,但是没有头绪时则会感觉一头雾水。

expr的设计思路是,将jQuery的所有类型的过滤条件,就均分成四组关键字拼接在一起的,如下表:

m1

过滤条件的类型

m2

 

m3 m4 示例
'' 标签名称 标签名称 —— —— div ; div p
'.' 标签的CSS类名 CSS类名 —— —— .red
'#' 标签的id属性 id属性值 —— —— #bt
'@' 标签的指定属性 属性名称 比较条件(可以指定也可以不指定) 属性的值(可以指定也可以不指定) @add='yy' ; @blob
':' 按照判定条件 判定条件名称 判定的值 —— :gt(10) ; :parent
' [ ' 获取长度属性 标签名称 —— —— [div

于是我们有expr的抽象结构如下

expr:{
    m1:"m2",
    m1:{
        m2:"m3 m4",
        m2:"m3"
    }
}

至此filter函数算是完成了。在分析filter的相关代码发现expr的'['过滤器中用到了jQuery.find函数。于是查看jQuery.find的代码定义。

我觉得这里的merge 和 map函数也属于 extend 和 each 类型的函数,对jQuery其它功能的实现提供基本支撑,需要记熟到心里面去。

标签: jquery, filter, grep, merge, map
Posted by klvoek @ 2012-2-12 15:15:49 阅读(82) 评论(0)
上一篇:(六)jQuery.extend代码段
下一篇:(八)jQuery.extend代码段

我也来参与讨论

你还可以输入600/600个字符 发表评论
称呼: (必填) 登录 | 开通博客
邮箱: (选填) 你的邮箱地址不会被公开
网站: (选填)
验证码: (必填)
看不清换一张 看不清楚换一张