测试代码1:
var x = { name:"CodePlayer", age: 20 }; var y = { age: 18 }; var z = { site: "www.365mini.com", age: 21 }; var obj = $.extend( x, y, z ); 这种调用下,target就是arguments[0]也就是x对象isArray函数源码分析:
isArray: Array.isArray || function( obj ) { return jQuery.type(obj) === "array"; },测试代码2:
$(document).ready(function(){ var x = { name:"CodePlayer", students:[1,2,3] }; var y = { name: "xxx",students:[3,4,5]}; alert(jQuery.extend(true,x,y).students);//返回3,4,5,因为两个students的键名是一样的都是数组下标,所以会覆盖! //迭代x,y时候都会迭代出来students集合,然后y的studenets就是isArray返回true //这时候target就是x对象, });测试代码3:
var x = [1,2,3]; var y = [3,4,5]; alert(jQuery.extend(true,x,y));//输出3,4,5 因为上面的for..in循环每次迭代出来的name是0,1,2也就是数组的下标,所以会后面的不断覆盖前面的!当对象中出现了数组属性等时候就会是这种情况和测试代码2是一样的道理!测试代码4:
var x = {name:"qinliang",age:12,length:2} var y = {name:"xxx",age:23,sex:"female",length:3} var result=jQuery.extend(true,x,y); for(var name in result) { alert(name+"->"+result[name]);//会打印sex } 它是以options也就是第二个y迭代的,x作为target,所以当把name,age,length迭代完了以后还要跌倒y的sex属性。src=target[name],copy=options[name],name是sex,这时候copy就不是undefined,而是female,这时候直接封装到target上面就可以了!测试代码5:
var x = [1,2,3] var y = [7,8,9,10] var result=jQuery.extend(true,x,y); for(var name in result) { alert(name+"->"+result[name]);//输出7.8.9.10因为for in迭代出来的name是0,1,2,3,前面三个下标被覆盖,后面一个下标直接添加到target中! }测试代码6:
var x = { sayHi: function(){ alert("这是新增的测试方法"); } }; // 只有一个参数,则表示省略target参数,target参数默认为jQuery对象本身 var obj = $.extend( x ); alert( obj === $ ); // true $.sayHi(); // 这是新增的测试方法 //target就是作为基对象用来扩展属性和方法的,如果没有第一个boolean参数,那么就是第一个参数否则为第二个参数 var x = { name:"CodePlayer", age: 20 }; var y = { age: 18 }; var z = { site: "www.365mini.com", age: 21 }; var obj = $.extend( x, y, z ); alert( obj === x ); // true });
测试代码7:
如果target不是一个对象,同时target也不是一个函数,那么把target设为空对象{} alert($.extend(1,2));第一个不是boolean,target就是1,但是1不是object也就是typeof 1不是[object Object] alert(typeof 1);是numberjQuery源码分析:
jQuery.extend = jQuery.fn.extend = function() { var src, copyIsArray, copy, name, options, clone, //获取第一个参数 target = arguments[0] || {}, i = 1, //获取参数的个数 length = arguments.length, //默认是浅复制 deep = false; // Handle a deep copy situation //如果第一个参数是boolean类型,那么把该参数赋值给局部变量deep if ( typeof target === "boolean" ) { deep = target; // skip the boolean and the target //target默认保存的是第一个参数,如果第一个参数是boolean,那么直接保存为第二个参数,只是为了匹配调用方式一和调用方式二的! target = arguments[ i ] || {}; i++; } // 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 //如果只有一个参数,这时候i就是1,length也就是1,那么把target设置为调用者,也就是jQuery对象本身!同时把i递减为0 //alert($.extend(1)); if ( i === length ) { target = this;//这里this就是jQuery,因为是jQuery调用的! i--; } //循环传递进来的参数集合 for ( ; i < length; i++ ) { // Only deal with non-null/undefined values //获取逐个参数 if ( (options = arguments[ i ]) != null ) { // Extend the base object //把每一个object类型参数的属性迭代出来 for ( name in options ) { src = target[ name ]; //获取当前迭代的options的同名属性值,因为options是一直变化迭代出来的对象,但是target就要返回的对象,所以他是“静态的” copy = options[ name ]; // Prevent never-ending loop //如果原有属性和现在迭代出来的属性一样,如上面例子中x和y,z都有age属性!如果相同那么继续下面的循环,例如如果age都是等于18就继续下一次循环! if ( target === copy ) { continue; } // Recurse if we're merging plain objects or arrays //copy是当前迭代出来的属性值,deep是否表示深度克隆,如果当前迭代出来的还是{}或者new Object构造的对象或者是Array类型或者类数组类型 //那么表示要深度克隆 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) //如果是类数组对象,[1,2,3] if ( copyIsArray ) { copyIsArray = false; //如果target原来的name属性是Array,那么clone就是原来的Array //下面调用target[ name ] = jQuery.extend( deep, clone, copy ); //也就是说会把clone作为target继续递归调用,把递归调用的结果设置为刚才的students的集合 //所以后面的copy还是会覆盖前面的clone clone = src && jQuery.isArray(src) ? src : []; } else { //不是类数组对象 clone = src && jQuery.isPlainObject(src) ? src : {}; } // Never move original objects, clone them //让clone作为target,把copy上面的属性全部克隆到clone上面去!也就是全部克隆到clone上去 target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values } else if ( copy !== undefined ) {//假如deep是false,上面的if就不会满足,那么就是浅层次克隆,那么结果就是后面的覆盖掉前面的属性 //因为这种直接赋值就会导致覆盖,给了target对象相同的属性 target[ name ] = copy; } }} // Return the modified object return target; };
总结:
(1)第一个参数表示是否深度克隆,如果是深度克隆就会把数组或者对象拆分出属性,然后遍历属性而不是直接覆盖!
(2)参数deep
的默认值为false,你可以为该参数明确指定true值,但不能明确指定false值。简而言之,第一个参数不能为false值。如果参数为null或undefined,则该参数将被忽略。
(3)如果只为$.extend()
指定了一个参数,则意味着参数target
被省略。此时,target
就是jQuery对象本身。通过这种方式,我们可以为全局对象jQuery添加新的函数。只有一个参数那么就会满足i===length,所以target就是this了!这时候i为0,表示用this继承第一个参数属性!
(4)底层的深度克隆的案例就是上面的测试代码2.