所谓 深浅拷贝:
对于仅仅是复制了引用(地址),换句话说,复制了之后,原来的变量和新的变量指向同一个东西,彼此之间的操作会互相影响,为 浅拷贝。
而如果是在堆中重新分配内存,拥有不同的地址,但是值是一样的,复制后的对象与原来的对象是完全隔离,互不影响,为 深拷贝。
深浅拷贝 的主要区别就是:复制的是引用(地址)还是复制的是实例。
1 //浅复制 2 function shallowCopy(src){ 3 var dst = {}; 4 for(var key in src){ 5 if(src.hasOwnProperty(key)){ 6 dst[key] = src[key]; 7 } 8 } 9 return dst;10 }11 12 var obj = {13 a : 1,14 arr : [2,3]15 };16 var shallowObj = shallowCopy(obj);17 console.log(obj === shallowObj); //false18 shallowObj.arr[1] = 5;19 console.log(obj.arr[1]); // = 520 console.log(obj); //{ a: 1, arr: [ 2, 5 ] }21 console.log(shallowObj); //{ a: 1, arr: [ 2, 5 ] }
//利用 递归 来实现深复制,对属性中所有引用类型的值,遍历到是基本类型的值为止。function deepCopy(src){ if(!src && typeof src !== 'object'){ return; } var dst = src.constructor === Array ? []:{}; for(var key in src){ if(src.hasOwnProperty(key)){ if(src[key] && typeof src[key] === 'object'){ dst[key] = src[key].constructor === Array ? []:{}; dst[key] = deepCopy(src[key]); //递归 }else{ dst[key] = src[key]; } } } return dst;}var a = { name:"jack", age:20};var b = deepCopy(a);console.log(a === b); //falsea.age = 30;console.log(a); //{ name: 'jack', age: 30 }console.log(b); //{ name: 'jack', age: 20 }
深拷贝浅拷贝问题探究:
1. Array 的 slice 和 concat 方法
两者都会返回一个新的数组实例。
//slicevar a = [1,2,3];var b = a.slice(); //slice console.log(b === a);a[0] = 4; console.log(a); //[ 4, 2, 3 ] console.log(b); //[ 1, 2, 3 ]//concatvar a = [1,2,3];var b = a.concat(); //concat console.log(b === a);a[0] = 4; console.log(a); //[ 4, 2, 3 ] console.log(b); //[ 1, 2, 3 ]
看起来很像深拷贝,但实际上不是!!!
//slicevar a = [[1,2,3],4,5];var b = a.slice(); console.log(a === b); //falsea[0][0] = 6; console.log(a); //[ [ 6, 2, 3 ], 4, 5 ] console.log(b); //[ [ 6, 2, 3 ], 4, 5 ]//concatvar a = [[1,2,3],4,5];var b = a.concat(); //concat console.log(b === a); //falsea[0][0] = 6; console.log(a); //[ [ 6, 2, 3 ], 4, 5 ] console.log(b); //[ [ 6, 2, 3 ], 4, 5 ]
Array的 slice 和 concat 方法 并不是 真正的深拷贝,他们其实是浅拷贝。 递归复制是无法完成的。
2. jQuery中的 extend 复制方法
可以用来扩展对象,这个方法可以传入一个参数:deep(true or false),表示是否执行深复制(如果是深复制则会执行递归复制)。
//jQuery环境下运行 //深复制 var obj = { name:'xixi', age:20, company : { name : '腾讯', address : '深圳'} };var obj_extend = $.extend(true,{}, obj); //extend方法,第一个参数为true,为深拷贝,为false,或者没有为浅拷贝。console.log(obj === obj_extend); //falseobj.company.name = "ali";obj.name = "hei";console.log(obj); //{ name:'hei',age:20,company : { name : 'ali', address : '深圳'} } console.log(obj_extend); //{name:'xixi',age:20,company : { name : '腾讯', address : '深圳'} }
//jQuery环境下运行 //浅复制 var obj = {
name:"xixi", age:20, company : { name : '腾讯', address : '深圳'} };var obj_extend = $.extend(false,{}, obj); //extend方法,第一个参数为true,为深拷贝,为false,或者没有为浅拷贝。console.log(obj === obj_extend); //falseobj.name = "heihei";console.log(obj); //{ name:'heihei',age:20,company : { name : '腾讯', address : '深圳'} console.log(obj_extend); //{name:'xixi',age:20,company : { name : '腾讯', address : '深圳'}
总结:Array 的 slice 和 concat 方法 和 jQuery 中的 extend 复制方法,他们都会复制第一层的值,对于 第一层 的值都是 深拷贝,而到 第二层 的时候 Array 的 slice 和 concat 方法就是 复制引用 ,jQuery 中的 extend 复制方法 则 取决于 你的 第一个参数, 也就是是否进行递归复制。所谓第一层 就是 key 所对应的 value 值是基本数据类型,也就像上面栗子中的name、age,而对于 value 值是引用类型 则为第二层,也就像上面例子中的 company。
3. JSON 对象的 parse 和 stringify
JOSN 对象中的 stringify 可以把一个 js 对象序列化为一个 JSON 字符串,parse 可以把 JSON 字符串反序列化为一个 js 对象,这两个方法实现的是深拷贝。
var obj = { name:'xixi', age:20, company : { name : '腾讯', address : '深圳'} };var obj_json = JSON.parse(JSON.stringify(obj));console.log(obj === obj_json); //falseobj.company.name = "ali";obj.name = "hei";console.log(obj); //{ name: 'hei', age: 20, company: { name: 'ali', address: '深圳' } }console.log(obj_json); //{ name: 'xixi', age: 20, company: { name: '腾讯', address: '深圳' } }