二、构造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; };

浙公网安备 33010602011771号