由于JavaScript存储对象都是存地址的,因此浅复制会致使 obj 和obj1 指向同一块内存地址。个人理解是,这有点相似数据双向绑定,改变了其中一方的内容,都是在原来的内存基础上作修改会致使拷贝对象和源对象都发生改变,而深复制通常都是开辟一块新的内存地址,将原对象的各个属性逐个复制出去。对拷贝对象和源对象各自的操做不影响另外一方javascript
//数组拷贝 //浅复制,双向改变,指向同一片内存空间 let arr = [1, 2, 3]; let arr1 = arr; arr1[1] = 'test'; console.log('shallow copy: ' + arr + " " + arr1); //shallow copy: 1,test,3 1,test,3 //深复制,开辟新的内存区 //方法一:slice,原理:slice返回一个新数组 let deepArr = [1, 2, 3]; let deepArr1 = deepArr.slice(0); deepArr1[1] = 'test'; console.log('deep copy: ' + deepArr + " " + deepArr1); //deep copy: 1,2,3 1,test,3 //方法二:concat,原理:concat返回一个新数组 let deepArr2 = [1, 2, 3]; let deepArr3 = deepArr2.concat(); deepArr3[1] = 'test'; console.log('deep copy: ' + deepArr2 + " " + deepArr3); //deep copy: 1,2,3 1,test,3 //知乎看到的深复制方法,这个函数能够深拷贝 对象和数组,很遗憾,对象里的函数会丢失 deepCloneObj = obj => { let str, newobj = obj.constructor === Array ? [] : {}; if(typeof obj !== 'object') { return; }else if(window.JSON) { /*好处是很是简单易用,可是坏处也显而易见,会丢失不少东西,这会抛弃对象的constructor, 也就是深复制以后,不管这个对象本来的构造函数是什么,在深复制以后都会变成Object。 另外诸如RegExp对象是没法经过这种方式深复制的。 */ str = JSON.stringify(obj); newobj = JSON.parse(str); //console.log(JSON.parse(JSON.stringify(/[0-9]/))); }else { for(let i in obj) { newobj[i] = typeof obj[i] === 'object' ? deepCloneObj(obj[i]) : obj[i]; } } return newobj; } let deepArr4 = { a: 1, b: 'test', c: [1, 2, 3], d: { 'a': 'd:a', 'b': 'd:b' } } deepArr5 = deepCloneObj(deepArr4); deepArr5['a'] = 'testa'; console.log('deep copy: ' + JSON.stringify(deepArr4) + " " + JSON.stringify(deepArr5)); /*deep copy: {"a":1,"b":"test","c":[1,2,3],"d":{"a":"d:a","b":"d:b"}} {"a":"testa","b":"test","c":[1,2,3],"d":{"a":"d:a","b":"d:b"}} */
1.jQuery.extend
第一个参数能够是布尔值,用来设置是否深度拷贝的:java
jQuery.extend(true, { a : { a : "a" } }, { a : { b : "b" } } ); jQuery.extend( { a : { a : "a" } }, { a : { b : "b" } } );
下面是源码,能够看看jquery
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 if ( typeof target === "boolean" ) { deep = target; // skip the boolean and the target 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 if ( i === length ) { 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 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 ) { target[ name ] = copy; } } } } // Return the modified object return target; };
2.lodash —— _.clone() / _.cloneDeep()
在lodash中关于复制的方法有两个,分别是_.clone()和_.cloneDeep()。其中_.clone(obj, true)等价于_.cloneDeep(obj)。使用上,lodash和jquery并无太大的区别,但看了源码会发现, jQuery 不过60多行。可 lodash 中与深复制相关的代码却有上百行.jQuery 没法正确深复制 JSON 对象之外的对象,lodash 花了大量的代码来实现 ES6 引入的大量新的标准对象。更厉害的是,lodash 针对存在环的对象的处理也是很是出色的。所以相较而言,lodash 在深复制上的行为反馈比jquery好不少,是更拥抱将来的一个第三方库。数组
综上所述,数组的深拷贝比较简单,方法没有什么争议,对象的深拷贝,比较好的方法是用lodash的方法实现,或者递归实现,比较简单的深复制可使用JSON.parse(JSON.stringify(obj))实现函数