- /*!
- * 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);
- //其它相关代码...(省略)
- }
- });
源码291~353行,jQuery.extend函数(utils)
jQuery.extend()使用方式有三种:
1.jQuery.extend({...}),参数为一个对象,这种使用方式是把参数的值cp给jQuery对象,这是扩展jQuery最常用的方式,后面很多代码都采用这种方式。
2.jQuery.extend({...},{...}...),参数为多个对象,这种方式是把后面的多个对象的值cp给第一个对象,如果后面对象有和前面对象相同的属性,则后面的覆盖前面的。
3.jQuery.extend(true,{a:{h:22,w:333}},{a:{h:552,s:2532},c:287}),深度cp,递归合并、覆盖、复制的方式。返回结果为{a:{h:552,w:333,s:2532},c:287}
接下来看源码:
- jQuery.extend = jQuery.fn.extend = function() {
- var options, name, src, copy, copyIsArray, clone,
- target = arguments[0] || {},//参数目标对象
- i = 1,
- length = arguments.length,//参数长度
- deep = false;//是否为深度复制
- // Handle a deep copy situation
- //如果为深度复制,则目标对象和原对象游标值i,以及深度值都进行更新
- if ( typeof target === "boolean" ) {
- deep = target;
- target = arguments[1] || {};
- // skip the boolean and the target
- i = 2;
- }
- // Handle case when target is a string or something (possible in deep copy)
- //当目标对象的值类型错误,则重置为{}
- if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
- target = {};
- }
- // extend jQuery itself if only one argument is passed
- //当参数值长度为1的情况下,目标对象就为jQuery自身
- if ( length === i ) {
- target = this;
- --i;
- }
- for ( ; i < length; i++ ) {
- // Only deal with non-null/undefined values
- if ( (options = arguments[ i ]) != null ) {//忽略空对象
- // Extend the base object
- for ( name in options ) {
- src = target[ name ];
- copy = options[ name ];//存储对象的值
- // Prevent never-ending loop
- if ( target === copy ) {
- continue;
- }
- // Recurse if we're merging plain objects or arrays
- //深度复制只有属性深度多于俩层的对象关系的结构的,如{a:{s:21,age:11}}或{a:['s'=>21,'age'=>11]}
- if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
- if ( copyIsArray ) {//如果是数组对象
- copyIsArray = false;
- clone = src && jQuery.isArray(src) ? src : [];
- } else {//普通对象
- clone = src && jQuery.isPlainObject(src) ? src : {};
- }
- // Never move original objects, clone them
- // 调用自身进行递归复制
- target[ name ] = jQuery.extend( deep, clone, copy );
- // Don't bring in undefined values
- } else if ( copy !== undefined ) {//非深度CP直接覆盖目标属性
- target[ name ] = copy;
- }
- }
- }
- }
- // Return the modified object
- return target;
- };