klvoek

klvoek

(九)jQuery.extend代码段

再次返回看init的代码,我抽离出一个init的简明结构:

init: function(){
    jQuery.initDone = true;
     
    jQuery.each( jQuery.macros.axis, function(i,n){
        jQuery.fn[ i ] = function(a) {
            ...
        };
    });
     
    jQuery.each( jQuery.macros.to, function(i,n){
        jQuery.fn[ i ] = function(){
            ...
        };
    });
     
    jQuery.each( jQuery.macros.each, function(i,n){
        jQuery.fn[ i ] = function() {
            ...
        };
    });
    jQuery.each( jQuery.macros.filter, function(i,n){
        jQuery.fn[ n ] = function(num,fn) {
            ...
        };
    });
     
    jQuery.each( jQuery.macros.attr, function(i,n){
        n = n || i;
        jQuery.fn[ i ] = function(h) {
            ...
        };
    });
    jQuery.each( jQuery.macros.css, function(i,n){
        jQuery.fn[ n ] = function(h) {
            ...
        };
    });
}

注意每一段each的第二个function参数,该function里定义了jQuery.fn上的属性。也就是说,这里所有的each都是在拓展jQuery对象上的功能。

先开始从第一个each代码开始看,init函数如何拓展jQuery对象上的功能。发现调用了pushStack,查看pushStack方法发现调用了each 和 get方法。这两个方法均在jQuery.fn = jQuery.prototype = { ... } 代码段的定义里。

each: function( fn, args ) {
    return jQuery.each( this, fn, args );
}
get: function( num ) {
    // Watch for when an array (of elements) is passed in     if ( num && num.constructor == Array ) {
        // Use a tricky hack to make the jQuery object         // look and feel like an array         this.length = 0;
        [].push.apply( this, num );
         
        return this;
    } else         return num == undefined ?
            // Return a 'clean' array             jQuery.map( this, function(a){ return a } ) :
            // Return just the object             this[num];
}
pushStack: function(a,args) {
    var fn = args && args[args.length-1];
    if ( !fn || fn.constructor != Function ) {
        if ( !this.stack ) this.stack = [];
        this.stack.push( this.get() );
        this.get( a );
    } else {
        var old = this.get();
        this.get( a );
        if ( fn.constructor == Function )
            return this.each( fn );
        this.get( old );
    }
    return this;
}

首先看each函数实现。该each函数仅仅是对jQuery.each的封装,隐含each作用对象为当前的jQuery对象。

再看get函数的实现。当向get传递的参数num为数组时,则将该数组(作为一个整体,而不是把数组中的值一个个的)入栈到jQuery对象上,并将入栈后的jQuery对象返回。当num参数为非数组时,如果未定义(一般为不传参时)则返回当前jQuery对象所有属性值的数组;如果定义了,则返回当前jQuery对象上此参数作为key的值。

最后看pushStack函数的实现。这里有一个问题,a参数一般会是什么值?经过研究许久发现,a参数一般会是数组值。所以,讨论当a是数组时pushStack的一般意义。为什么a大部分情况下是数组呢?查看pushStack的大部分引用代码就可以知道了:

 

jQuery.fn[ i ] = function(a) {
    var ret = jQuery.map(this,n);
    if ( a && a.constructor == String )
        ret = jQuery.filter(a,ret).r;
    return this.pushStack( ret, arguments );
};

如果fn未设置或者不为函数的话,则pushStack先将当前jQuery对象的DOM元素压入堆栈,然后将新的a压入当前的jQuery对象。最后将这个对象返回。这里需要再次对get函数进行说明。get函数影响了当前的jQuery对象的length属性,所以在目前没有找到明显定义或设置jQuery对象的length属性的代码之前get的调用值得注意。当fn为函数时pushStack将a通过this.get(a)保存到this上,并对this调用each处理,而this.get(old)则不会被执行。于是翻看未分析过的代码,发现最开始jQuery(a,c){ ... } 的代码中调用了get函数。结合jQuery(a,c){ ... } 是创建jQuery对象的主入口和一些以往使用jQuery的经验,摘录实现代码中几行引起我注意的代码:

// Handle HTML strings var m = /^[^<]*(<.+>)[^>]*$/.exec(a);if ( m ) a = jQuery.clean( [ m[1] ] );// Watch for when an array is passed in this.get( a.constructor == Array || a.length && !a.nodeType && a[0] != undefined && a[0].nodeType ?
    // Assume that it is an array of DOM Elements     jQuery.merge( a, [] ) :
    // Find the matching elements and save them for later     jQuery.find( a, c ) );

首先,jQuery.length的初始化以及jQuery对象的jQueryObject[index]是什么值,在最后一行比较大的代码里可以得到解读。由于这段代码处在创建jQuery对象的地方,所以创建jQuery对象之后则会因为调用了一次get函数而将length属性初始化,具体初始化的值要根据get( ... ) 中的表达式计算出的值来确定了。这一段表达式中的注释 // Assume that it is an array of DOM Elements 假设a是一个DOM元素的数组 我们可以看出这段表达式返回的是一个DOM元素数组。所以在对jQuery对象进行each遍历时,是遍历的这个DOM元素数组。要记得each中对于有length属性的参数obj是采用数组遍历的形式的。这里,我们也要对find和filter的返回值有一个总结:那就是返回一个DOM元素数组。

再看最开始两行对jQuery.clean函数的用法,在后面分析clean时需要分析到这两行代码。现在先解析一下传递给clean的是什么参数呢?第一行是一段正则表达式,而m[1]是正则表达式中第一个元组匹配的结果。还记不记得jQuery可以根据传入的一段html文本,并返回jQuery对象。没错,这里的m[1]就是匹配出你传入的html文本这种鬼东西。在接下来的分析clean代码时要记得这点。

我现在可以理解第一段each代码所实现的内容了:

jQuery.each( jQuery.macros.axis, function(i,n){
    jQuery.fn[ i ] = function(a) {
        var ret = jQuery.map(this,n);
        if ( a && a.constructor == String )
            ret = jQuery.filter(a,ret).r;
        return this.pushStack( ret, arguments );
    };
});

拓展jQuery对象身上的axis类方法,该方法主题过程是:按照方法属性即n指定的操作,获取当前jQuery对象所代表的DOM元素的指定DOM元素并以数组形式返回。如果设定了过滤条件,那么对获取的数组进行过滤。ret仍旧是DOM元素数组。最后将DOM数组pushStack会当前jQuery对象,并将该对象返回。这里需要注意的是,return pushStack(ret,arguments)操作并没有创建新的jQuery对象,而是将当前jQuery对象所对应的DOM元素替换为ret,并将老的DOM元素数组放入堆栈以便调用get方法可以再次取出。

第二段each代码的实现:

jQuery.each( jQuery.macros.to, function(i,n){
    jQuery.fn[ i ] = function(){
        var a = arguments;
        return this.each(function(){
            for ( var j = 0; j < a.length; j++ )
                $(a[j])[n]( this );
        });
    };
});

to中是进行的DOM节点插入操作。arguments是指这些操作的参数,可以是节点数组,即传入多个节点。这个each代码是进行的对给定参数中的每个节点调用当前jQuery对象所关联的DOM元素的指定方法。

第三段each代码的实现

jQuery.each( jQuery.macros.filter, function(i,n){
    jQuery.fn[ n ] = function(num,fn) {
        return this.filter( ":" + n + "(" + num + ")", fn );
    };
});

这一段代码是拓展jQuery对象上的过滤类方法。实现很简单,即是调用当前jQuery对象上的filter方法。

第四段each代码的实现

jQuery.each( jQuery.macros.attr, function(i,n){
    n = n || i;
    jQuery.fn[ i ] = function(h) {
        return h == undefined ?
            this.length ? this[0][n] : null :
            this.attr( n, h );
    };
});

这一段代码是拓展jQuery对象上的DOM节点属性操作方法。注意,这里在return时实现了指定h时设置指定属性,未指定时获取值的功能。

jQuery.each( jQuery.macros.css, function(i,n){
    jQuery.fn[ n ] = function(h) {
        return h == undefined ?
            ( this.length ? jQuery.css( this[0], n ) : null ) :
            this.css( n, h );
    };
});

这段代码是拓展jQuery对象的css类方法。实现了与属性一样的传入值则设置css属性否则获取css属性值的功能。我们看到jQuery.css 和 jQuery.fn.css(即this.css)我们还没有分析,于是


标签: jquery, pushStack, get
Posted by klvoek @ 2012-2-12 16:04:17 阅读(76) 评论(0)
上一篇:(八)jQuery.extend代码段
下一篇:(十)jQuery.extend代码段

我也来参与讨论

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