jQuery.extend = jQuery.fn.extend = function() {
     ...
};

我们可以用$.extend去扩展自定义的对象,如:

var myself = {name:jack};
$.extend(myself, {setName:function)(n){this.name=n;} });
myself.setName("tom");

通过$.extend为对象myself添加了setName方法。但这里主要讨论$.extend如何构建jquery库的。不知注意到上面代码中jQuery.extend和jQuery.fn.extend是同一个函数。我们浏览下jquery库,发现有些方法是通过调用jQuery.extend扩展的,有的则是通过调用jQuery.fn.extend扩展的。下面分别讨论:

1,通过jQuery.extend扩展

我们知道jQuery.extend中的jQuery类型是function,即typeof jQuery值为字符串“function”。如果把jQuery当成一个类,jQuery.extend相当于为该类添加了静态方法extend。静态方法是不需要new一个实例再去调用的,通过“类名+方法名”直接调用。即jQuery.extend(…),jQuery又被赋值给$。因此我们更习惯$.extend(…)。

源码中直接调用jQuery.extend方法,只传一个参数。如下:

jQuery.extend({
noConflict: function( deep ) {
window.$ = _$;
if ( deep )
window.jQuery = _jQuery;
return jQuery;
},
...
});

我们知道extend中如果只传一个参数,那么将执行该句:

target = this;
[/js]

即扩展自己,而这里的this则是function jQuery。也就是说给function jQuery添加了许多静态方法,这些方法都可以直接通过jQuery.xx(或$.xx)方式来调用,而不是先执行(调用)jQuery方法再去调用xx,如$(“#id”).xx。也许下面这个例子更容易理解:

function fun(){}//定义一个类(函数)

//为该类(函数)添加一个静态方法extend
fun.extend=function(obj){
for(var a in obj)
this[a] = obj[a];//注意:这里的tihs即fun
}

//调用extend为该类添加了静态属性name,静态方法method1
fun.extend({name:"fun",method1:function(){}});

//输出name,prototype,extend,method1
console.dir(fun)

因此,jquery中的isFunction, isArray, trim等都是静态方法,只能通过$.isFunction, $.isArray, $.trim来调用。而不能通过$(“…”).isFuction, $(“…”).isArray, $(“…”).trim调用。

2,通过jQuery.fn.extend扩展

jQuery.fn等于jQuery.prototype,也就是说给function jQuery的原型(prototype)上挂了个extend方法。通过调用jQuery.fn.extend(object)来扩展时(注意只传一个参数object),extend函数中仍然会执行

target = this;

而这时的this则是jQuery.prototype(上面则是jQuery函数自身)。即给jQuery.prototype上添加了许多属性,方法。当jQuery函数执行时,如$()或jQuery(),更多时候是$()。该函数会new一个jQuery(见上一篇jquery对象的组成)。这时则把扩展的属性,方法都附加到new生成的对象上了。也许下面这个示例更容易理解:

function fun(){}//定义一个类(函数)

//给该类原型上添加一个方法extned
fun.prototype.extend = function(obj){
	for(var a in obj)
		this[a] = obj[a];//注意:这里的this即是fun.prototype
}			

//调用extend方法给fun.prototype上添加属性,方法
fun.prototype.extend({name:"fun2",method1:function(){}})

//输出name,extend,method1
console.dir(new fun())

因此扩展的属性或方法都添加到jquery对象上了。
如bind, one, unbind等可以通过$(“…”).bind, $(“…”).one, $(“…”).unbind方式调用。却不能通过 $.bind, $.one, $.unbind方式调用。

jquery库与prototype库一样都是通过extend方法扩展出整个库的。相对来说jqueyr的扩展方式更难理解一些。
总结如下:
a,jQuery.extend({…})是给function jQuery添加静态属性或方法
b,jQuery().extend({…})是给jquery对象添加属性或方法。

ps:这里的jQuery即为$。

最后附一个jq1.4 extend源码分析:

/*!
 * jQuery源码分析-extend函数
 * jQuery版本:1.4.2
 * 
 * ----------------------------------------------------------
 * 函数介绍
 * jQuery.extend与jQuery.fn.extend指向同一个函数对象
 * jQuery.extend是jQuery的属性函数(静态方法)
 * jQuery.fn.extend是jQuery函数所构造对象的属性函数(对象方法)
 *
 * ----------------------------------------------------------
 * 使用说明
 * extend函数根据参数和调用者实现功能如下:
 * 1.对象合并:
 * 对象合并不区分调用者,jQuery.extend与jQuery.fn.extend完全一致
 * 也就是说对jQuery对象本身及jQuery所构造的对象没有影响
 * 对象合并根据参数区分,参数中必须包括两个或两个以上对象
 * 如:$.extend({Object}, {Object}) 或 $.extend({Boolean},{Object}, {Object})
 * 对象合并返回最终合并后的对象,支持深度拷贝
 * 
 * 2.为jQuery对象本身增加方法:
 * 这种方式从调用者和参数进行区分
 * 形式为 $.extend({Object})
 * 这种方式等同于 jQuery.{Fnction Name}
 * 
 * 3.原型继承:
 * 原型继承方式可以为jQuery所构造的对象增加方法
 * 这种方式也通过调用者和参数进行区分
 * 形式为 $.fn.extend({Object})
 * 这种方式实际上是将{Object}追加到jQuery.prototype,实现原型继承
 * 
 * ----------------------------------------------------------
 * 
 */
 
// jQuery.fn = jQuery.prototype
// jQuery.fn.extend = jQuery.prototype.extend
jQuery.extend = jQuery.fn.extend = function(){

    //目标对象
    var target = arguments[0] || {},    
    
    //循环变量,它会在循环时指向需要复制的第一个对象的位置,默认为1
    //如果需要进行深度复制,则它指向的位置为2
    i = 1,    
    
    //实参长度
    length = arguments.length,    
    
    //是否进行深度拷贝
    //深度拷贝情况下,会对对象更深层次的属性对象进行合并和覆盖
    deep = false,    
    
    //用于在复制时记录参数对象
    options,    
    
    //用于在复制时记录对象属性名
    name,    
    
    //用于在复制时记录目标对象的属性值
    src,    
    
    //用于在复制时记录参数对象的属性值
    copy;
    
    //只有当第一个实参为true时,即需要进行深度拷贝时,执行以下分支
    if (typeof target === "boolean") {
        //deep = true,进行深度拷贝
        deep = target;
        
        //进行深度拷贝时目标对象为第二个实参,如果没有则默认为空对象
        target = arguments[1] || {};
        
        //因为有了deep深度复制参数,因此i指向的位置为第二个参数
        i = 2;
    }
    
    //当目标对象不是一个Object且不是一个Function时(函数也是对象,因此使用jQuery.isFunction进行检查)
    if (typeof target !== "object" && !jQuery.isFunction(target)) {
        
        //设置目标为空对象
        target = {};
    }
    
    //如果当前参数中只包含一个{Object}
    //如 $.extend({Object}) 或 $.extend({Boolean}, {Object})
    //则将该对象中的属性拷贝到当前jQuery对象或实例中
    //此情况下deep深度复制仍然有效
    if (length === i) {
        
        //target = this;这句代码是整个extend函数的核心
        //在这里目标对象被更改,这里的this指向调用者
        //在 $.extend()方式中表示jQuery对象本身
        //在 $.fn.extend()方式中表示jQuery函数所构造的对象(即jQuery类的实例)
        target = this;
        
        //自减1,便于在后面的拷贝循环中,可以指向需要复制的对象
        --i;
    }
	
	//循环实参,循环从第1个参数开始,如果是深度复制,则从第2个参数开始
    for (; i < length; i++) {
        
        //当前参数不为null,undefined,0,false,空字符串时
        //options表示当前参数对象
        if ((options = arguments[i]) != null) {
            
            //遍历当前参数对象的属性,属性名记录到name
            for (name in options) {
                
                //src用于记录目标对象中的当前属性值
                src = target[name];
                
                //copy用于记录参数对象中的当前属性值
                copy = options[name];
                
                //存在目标对象本身的引用,构成死循环,结束此次遍历
                if (target === copy) {
                    continue;
                }
                
                //如果需要进行深度拷贝,且copy类型为对象或数组
                if (deep && copy && (jQuery.isPlainObject(copy) || jQuery.isArray(copy))) {
                
                    //如果src类型为对象或数组,则clone记录src
                    //否则colne记录与copy类型一致的空值(空数组或空对象)
                    var clone = src && (jQuery.isPlainObject(src) || jQuery.isArray(src)) ? src : jQuery.isArray(copy) ? [] : {};
                    
                    //对copy迭代深度复制
                    target[name] = jQuery.extend(deep, clone, copy);
                    
                    //如果不需要进行深度拷贝
                } else if (copy !== undefined) {
                    
                    //直接将copy复制给目标对象
                    target[name] = copy;
                }
            }
        }
    }
    
    //返回处理后的目标对象
    return target;
};


/**
 * jQuery框架本身对extend函数的使用非常频繁
 * 典型示例为jQuery.ajax
 * 
 */

//使用extend对jQuery对象本身进行扩展,只给了一个参数对象
//该对象中的属性将被追加到jQuery对象中
jQuery.extend({
 
    //jQuery.ajax
    //$.ajax
 
    //这里的origSettings参数是自定义的ajax配置
    //jQuery对象本身有一个ajaxSettings属性,是默认的ajax配置
    ajax: function(origSettings){ 
 
        //这里使用extend对ajax配置项进行合并
        //第一个参数表示进行深度拷贝
        //首先将第3个参数jQuery.ajaxSettings(即jQuery默认ajax配置)复制到第2个参数(一个空对象)
        //然后将第4个参数(自定义配置)复制到配置对象(覆盖默认配置)
        //这里的s就得到了最终的ajax配置项
        var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings);
  
        //其它相关代码...(省略)
    }
}); 

4 thoughts on “jquery扩展的2种方法比较

  1. HI:
    你对jQuery.extend讲解得非常详细,但是有一点我比较疑惑:
    “如bind, one, unbind等可以通过$(“…”).bind, $(“…”).one, $(“…”).unbind方式调用。却不能通过 $.bind, $.one, $.unbind方式调用。”

    如果这些函数已经存在与 prototype中,那么其实例化虽然不存在,但是依旧可以通过作用域链访问到。

    Reply
    • 不好意思,我理解错了,因为$不是一个实例化对象,而是一个模拟类,无法像类一个通过作用域链获得函数。如以下

      a = function(){};
      a.prototype.b = function(){
      console.log('hi');
      };
      a.c = function(){console.log('c')};
      a.c();//Run success!
      a.b();//Run error,a.b is not a function

      Reply
      • 嗯 需要 new 一下才行。

        a = function(){};
        a.prototype.b = function(){
        console.log(‘hi’);
        };
        a.c = function(){console.log(‘c’)};
        a.c();//Run success!
        var d = new a();
        d.b();//Run success!

        Reply
    • jQuery.extend 扩展的是静态方法,不需要对象实例的。
      jQuery.fn.extend 是实例化后的。

      你可以把这2个理解为不同的函数,只是名称有点像。这样可能好理解一点。

      Reply

Leave a reply

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> 

required