klvoek

klvoek

(八)jQuery.extend代码段

分析jQuery.find的实现代码,发现其调用了 getAll 、trim、sliding三个方法。

getAll: function(o,r) {
    r = r || [];
    var s = o.childNodes;
    for ( var i = 0; i < s.length; i++ )
        if ( s[i].nodeType == 1 ) {
            r.push( s[i] );
            jQuery.getAll( s[i], r );
        }
    return r;
}
trim: function(t){
    return t.replace(/^\s+|\s+$/g, "");
}
sibling: function(elem, pos, not) {
    var elems = [];
    var siblings = elem.parentNode.childNodes;
    for ( var i = 0; i < siblings.length; i++ ) {
        if ( not === true && siblings[i] == elem ) continue;
        if ( siblings[i].nodeType == 1 )
            elems.push( siblings[i] );
        if ( siblings[i] == elem )
            elems.n = elems.length - 1;
    }
    return jQuery.extend( elems, {
        last: elems.n == elems.length - 1,
        cur: pos == "even" && elems.n % 2 == 0 || pos == "odd" && elems.n % 2 || elems[pos] == elem,
        prev: elems[elems.n - 1],
        next: elems[elems.n + 1]
    });
}

getAll的定义为,将o对象的所有子级节点获取到r集合中。代码内使用了递归调用。

trim则为通过正则表达式将字符串首尾的连续空白字符移除。

sibling为获取兄弟节点的意思。not参数表示是否将自己也包含仅结果结合中,为true时则表示不包含,否则包含进结果集合里。注意该函数实现代码里在return时,拓展了结果集合的几个属性。last属性-表示自己是否是兄弟节点中的最后一个。cur属性-表示自己是否出现在指定的位置,位置属性有三种:奇数位置、偶数位置、指定位置。pref属性-表示自己的前一个兄弟节点。next属性-表示自己的后一个兄弟节点。

接下来我们看find函数的实现代码:

find: function( t, context ) {
    // Make sure that the context is a DOM Element     if ( context && context.nodeType == undefined )
        context = null;
    // Set the correct context (if none is provided)     context = context || jQuery.context || document;
    if ( t.constructor != String ) return [t];
    if ( !t.indexOf("//") ) {
        context = context.documentElement;
        t = t.substr(2,t.length);
    } else if ( !t.indexOf("/") ) {
        context = context.documentElement;
        t = t.substr(1,t.length);
        // FIX Assume the root element is right :(         if ( t.indexOf("/") >= 1 )
            t = t.substr(t.indexOf("/"),t.length);
    }
    var ret = [context];
    var done = [];
    var last = null;
    while ( t.length > 0 && last != t ) {
        var r = [];
        last = t;
        t = jQuery.trim(t).replace( /^\/\//i, "" );
         
        var foundToken = false;
         
        for ( var i = 0; i < jQuery.token.length; i += 2 ) {
            var re = new RegExp("^(" + jQuery.token[i] + ")");
            var m = re.exec(t);
             
            if ( m ) {
                r = ret = jQuery.map( ret, jQuery.token[i+1] );
                t = jQuery.trim( t.replace( re, "" ) );
                foundToken = true;
            }
        }
         
        if ( !foundToken ) {
            if ( !t.indexOf(",") || !t.indexOf("|") ) {
                if ( ret[0] == context ) ret.shift();
                done = jQuery.merge( done, ret );
                r = ret = [context];
                t = " " + t.substr(1,t.length);
            } else {
                var re2 = /^([#.]?)([a-z0-9\\*_-]*)/i;
                var m = re2.exec(t);
     
                if ( m[1] == "#" ) {
                    // Ummm, should make this work in all XML docs                     var oid = document.getElementById(m[2]);
                    r = ret = oid ? [oid] : [];
                    t = t.replace( re2, "" );
                } else {
                    if ( !m[2] || m[1] == "." ) m[2] = "*";
     
                    for ( var i = 0; i < ret.length; i++ )
                        r = jQuery.merge( r,
                            m[2] == "*" ?
                                jQuery.getAll(ret[i]) :
                                ret[i].getElementsByTagName(m[2])
                        );
                }
            }
        }
        if ( t ) {
            var val = jQuery.filter(t,r);
            ret = r = val.r;
            t = jQuery.trim(val.t);
        }
    }
    if ( ret && ret[0] == context ) ret.shift();
    done = jQuery.merge( done, ret );
    return done;
}

find函数的代码也比较多,粗略浏览下来注意到查找Token的循环与filter中查找parse的循环类似。find函数中引用到了jQuery.token属性,查看jQuery.token代码如下:

token: [
    "\\.\\.|/\\.\\.", "a.parentNode",
    ">|/", "jQuery.sibling(a.firstChild)",
    "\\+", "jQuery.sibling(a).next",
    "~", function(a){
        var r = [];
        var s = jQuery.sibling(a);
        if ( s.n > 0 )
            for ( var i = s.n; i < s.length; i++ )
                r.push( s[i] );
        return r;
    }
]

审视token中的值定义,发现token中定义的内容类似XPath的作用。多次阅读find代码之后发更加肯定这一点。按照filter中使用的方法给find划分逻辑结构如下:

  • 处理find作用的对象和作用条件。

循环开始<直到没有了查找条件>

  • 获取查询集合,传递给filter根据后续条件进行过滤。
    1. 获取token,如果获取到token则获取token指定的集合
    2. 在没有获取到token时,检查是否是条件分隔符','和'|',如果是则将之前处理的结果保存到最终结果里,并初始画当前集合。检查是否是作用于全局的查找条件(按照id查找,按照标签名查找,所有子节点),获取集合。
  • 在获取的集合上应用过滤条件

循环结束

处理find作用对象和作用条件:

// Make sure that the context is a DOM Element if ( context && context.nodeType == undefined )
    context = null;// Set the correct context (if none is provided) context = context || jQuery.context || document;if ( t.constructor != String ) return [t];if ( !t.indexOf("//") ) {
    context = context.documentElement;
    t = t.substr(2,t.length);
} else if ( !t.indexOf("/") ) {
    context = context.documentElement;
    t = t.substr(1,t.length);
    // FIX Assume the root element is right :(     if ( t.indexOf("/") >= 1 )
        t = t.substr(t.indexOf("/"),t.length);
}var ret = [context]; //定义了存储中间结果的变量 var done = []; // 存储最终结果的变量 var last = null; //存储上一次处理的条件

如上代码中,前三行代码操作了context参数。主要操作为首先判断传入的context是不是正确的html节点。如果不是的话将context设置为null。接下来对context赋值,这里需要注意jQuery.contex在哪里定义的呢?

剩下的代码是对find的作用条件t进行处理。当传入的t不是字符串时则直接返回[t]。当t是字符串时,如果t开头为'//' 或'/' 则将context设置为当前文档的根节点。需要注意代码中注释了 //FIX Assume the root element is right Sad的代码,这两句代码起到什么效果呢?

紧跟后面是几个内部变量的定义。

循环开始的条件while ( t.length > 0 && last != t ) { ... } 。这表示当作用条件为空或者作用条件不能再被识别时结束(注意last的意义)。

获取查询集合,传递给filter根据后续条件进行过滤。

获取token。用到正则表达式匹配token。

var r = [];
last = t;
t = jQuery.trim(t).replace( /^\/\//i, "" );var foundToken = false;for ( var i = 0; i < jQuery.token.length; i += 2 ) {
    var re = new RegExp("^(" + jQuery.token[i] + ")");
    var m = re.exec(t);
     
    if ( m ) {
        r = ret = jQuery.map( ret, jQuery.token[i+1] );
        t = jQuery.trim( t.replace( re, "" ) );
        foundToken = true;
    }
}

如果没有获取到token

if ( !foundToken ) {
    if ( !t.indexOf(",") || !t.indexOf("|") ) {
        if ( ret[0] == context ) ret.shift();
        done = jQuery.merge( done, ret );
        r = ret = [context];
        t = " " + t.substr(1,t.length);
    } else {
        var re2 = /^([#.]?)([a-z0-9\\*_-]*)/i;
        var m = re2.exec(t);
        if ( m[1] == "#" ) {
            // Ummm, should make this work in all XML docs             var oid = document.getElementById(m[2]);
            r = ret = oid ? [oid] : [];
            t = t.replace( re2, "" );
        } else {
            if ( !m[2] || m[1] == "." ) m[2] = "*";
            for ( var i = 0; i < ret.length; i++ )
                r = jQuery.merge( r,
                    m[2] == "*" ?
                        jQuery.getAll(ret[i]) :
                        ret[i].getElementsByTagName(m[2])
                );
        }
    }
}

在获取的集合上应用过滤条件

if ( t ) {
    var val = jQuery.filter(t,r);
    ret = r = val.r;
    t = jQuery.trim(val.t);
}

在这里需要思考find与filter的关系。我认为,find与filter存在着这样一种关系:find在filter之前被调用。也就是说,find判断filter所要作用的集合,filter在获取的集合上做过滤。

标签: jQuery, find, filter, token
Posted by klvoek @ 2012-2-12 15:28:38 阅读(81) 评论(0)
上一篇:(七)jQuery.extend代码段
下一篇:(九)jQuery.extend代码段

我也来参与讨论

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