二、构造jQuery对象4——jQuery.extend()、jQuery.fn.extend()

方法jQuery.extend()和jQuery.fn.extend()用于合并两个或多个对象的属性到第一个对象,它们的语法如下:
  jQuery.extend([deep],target,object1,[objectN])
  jQuery.extend([deep],target,object1,[objectN])
  deep参数是可选的布尔值,表示是否进行深度合并(即递归合并)。合并行为默认是不递归的,如果第一个参数的属性本身是一个对象或数组,它会被第二个或后面的其他参数的同名属性完全覆盖。如果为true,表示进行深度合并,合并过程是递归的。
  参数target是目标对象:参数object1和objectN是源对象,包含了待合并的属性。如果提供了两个或更多的对象,所有源对象的属性将会合并到目标对象;如果仅提供一个对象,意味着参数target被忽略,jQuery或jQuery.fn被当作目标对象,通过这个方式可以在jQuery或jQuery.fn上添加新的属性和方法,jQuery的其他模块大都是这么实现的。
  方法jQuery.extend()和jQuery.fn.extend()常用于编写插件和处理函数的参数。
  源码分析
  执行的关键步骤如下所示:
  修正参数deep、target、源对象的起始下标
  逐个遍历源对象:
  a.遍历源对象的属性
  b.覆盖目标对象的同名属性;如果是深度合并,则先递归调用jQuery.extend()
// 代码行:227——294
jQuery.extend = jQuery.fn.extend = function () {
    var options,
        // options,指向某个源对象
        name,
        // name,表示某个源对象的某个属性名
        src,
        // src,表示目标对象的某个属性的原始值
        copy,
        // copy,表示某个源对象的某个属性的值
        copyIsArray,
        // copyIsArray,指示变量copy是否是数组
        clone,
        // clone,表示深度复制时原始值的修正值
        target = arguments[0] || {},
        // target,指向目标对象
        i = 1,
        // i,表示源对象的起始下标
        length = arguments.length,
        // length,表示参数个数,用于修正变量target
        deep = false;
    // deep,指示是否执行深度复制,默认为false

    // Handle a deep copy situation
    // 如果第一个参数是布尔值,则修正第一个参数为deep,修正第二个参数为目标对象target,并且期望源对象从第三个开始。
    if (typeof target === "boolean") {
        deep = target;

        // Skip the boolean and the target
        target = arguments[i] || {};
        // 变量i的初始值为1,表示期望源对象从第2个元素开始;当第一个参数为布尔型时,变量i变为2,表示期望源对象从第3个元素开始。
        i++;
    }

    // Handle case when target is a string or something (possible in deep copy)
    // 如果目标对象target不是对象、不是函数,而是一个字符串或其他的基本类型,则统一换成为空对象{},因为在基本类型上设置非原生属性是无效的。
    if (typeof target !== "object" && !isFunction(target)) {
        target = {};
    }

    // Extend jQuery itself if only one argument is passed
    // 变量i表示源对象开始的下标,变量length表示参数个数,如果两者相等,表示期望的源对象没有传入,则把jQuery或jQuery.fn作为目标对象,并把源对象的开始下标减一,从而使得传入的对象被当做源对象。变量length和i相等有两种情况:
    // extend( object )  //只传入了一个参数。
    // extend( deep, object )  //传入两个参数,第一个参数是布尔值。
    if (i === length) {
        target = this;
        i--;
    }

    //逐个遍历源对象
    for (; i < length; i++) {

        // Only deal with non-null/undefined values
        // 遍历源对象的属性
        // arguments是一个类似数组的对象,包含了传入的参数,可以通过整数下标访问指定位置的参数。这行代码把获取源对象和对源对象的判断合并为一条语句,只有源对象不是null、undefined时才会继续执行。
        if ((options = arguments[i]) != null) {

            // Extend the base object
            // 开始遍历单个源对象属性
            for (name in options) {
                //变量src是原始值,变量copy是复制值。如果复制值copy与目标对象target相等,为了避免深度遍历时死循环,因此不会覆盖目标对象的同名属性。
                src = target[name];
                copy = options[name];

                // Prevent never-ending loop
                // 注意,判断target === copy时使用的是“===”,强制不做类型转换;如果使用“==”,则可能因自动类型转换而导致错误。
                if (target === copy) {
                    continue;
                }

                // Recurse if we're merging plain objects or arrays
                // 如果是深度合并且复制值copy是个JavaScript普通的对象或数组,则递归合并
                if (deep && copy && (jQuery.isPlainObject(copy) ||
                        (copyIsArray = Array.isArray(copy)))) {

                            //复制值copy是数组时,如果原始值src不是数组,则修正为空数组;复制值copy是普通JavaScript对象时,如果原始值src不是普通JavaScript对象,则修正为空对象{}。把原始值src或修正后的值赋值给值副本clone。
                    if (copyIsArray) {
                        copyIsArray = false;
                        clone = src && Array.isArray(src) ? src : [];

                    } else {
                        // 通过调用方法jQuery.isPlainObject(copy)判断复制值copy是否是“纯粹”的JavaScript对象,只有通过对象直接流量{}或new Object()创建的对象,才会返回true。
                        clone = src && jQuery.isPlainObject(src) ? src : {};
                    }

                    // Never move original objects, clone them
                    // 先把复制值copy递归合并到原始值副本clone中,然后覆盖目标对象的同名属性。
                    target[name] = jQuery.extend(deep, clone, copy);

                    // Don't bring in undefined values
                
                
                // 如果不是深度合并,并且复制值copy不是undefined,则直接覆盖目标对象的同名属性。
                } else if (copy !== undefined) {
                    target[name] = copy;
                }
            }
        }
    }

    // Return the modified object
    // 返回修改的对象
    return target;
};

 

posted @ 2019-04-01 14:08  道鼎金刚  阅读(146)  评论(0)    收藏  举报