深拷贝与浅拷贝的区别及使用方法
浅拷贝:两个对象之间相互影响(修改a对象的某个值,同时影响另外一个b对象的值)
深拷贝:两个对象之间互不干涉
一.浅拷贝的方法
1.for...in循环,只循环第一层
// 只复制第一层的浅拷贝 function simpleCopy(obj1) { var obj2 = Array.isArray(obj1) ? [] : {}; for (let i in obj1) { obj2[i] = obj1[i]; } return obj2; } var obj1 = { a: 1, b: 2, c: { d: 3 } } var obj2 = simpleCopy(obj1); obj2.a = 3; obj2.c.d = 4; alert(obj1.a); // 1 alert(obj2.a); // 3 alert(obj1.c.d); // 4 alert(obj2.c.d); // 4
2.直接赋值
let a=[0,1,2,3,4], b=a; console.log(a===b); a[0]=1; console.log(a,b);
3.Object.assign方法
- 只拷贝源对象的自身属性(不拷贝继承属性)
- 不会拷贝对象不可枚举的属性
- undefined和null无法转成对象,他们不能作为Object.assign参数,但是可以作为源对象
- 属性名为Symbol 值的属性,可以被Object.assign拷贝
var obj = { a: 1, b: 2 } var obj1 = Object.assign(obj); obj1.a = 3; console.log(obj.a) // 3
4.扩展运算符
第一层互不干涉,子层相互影响
// 当value是基本数据类型,比如String,Number,Boolean时,是可以使用拓展运算符进行深拷贝的
// 当value是引用类型的值,比如Object,Array,引用类型进行深拷贝也只是拷贝了引用地址,所以属于浅拷贝
var car = {brand: "BMW", price: "380000", length: "5米"}
var car1 = { ...car, price: "500000" }
console.log(car1); // { brand: "BMW", price: "500000", length: "5米" }
console.log(car); // { brand: "BMW", price: "380000", length: "5米" }
let obj = {a:1,b:{c:1}} let obj2 = {...obj} obj2.a = 4; console.log(obj.a) //1 console.log(obj2.a) //4 obj2.b.c = 5; console.log(obj.b.c) //5 console.log(obj2.b.c) //5
二.深拷贝
1.通过JSON对象来实现深拷贝(对象中的函数不能被拷贝)
var c =JSON.parse(JSON.stringify(obj)); c.push(4); console.log(a); //[1,2,3] console.log(c); //[1,2,3,4]
2.jquery的extend方法
$.extend( [deep ], target, object1 [, objectN ] )
deep表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝
target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
object1 objectN可选。 Object类型 第一个以及第N个被合并的对象。
var a=[1,2,3]; //深拷贝 var b=$.extend(true,[],a); a.push(4); console.log(a);//[1,2,3,4] console.log(b);//[1,2,3]
3.通过递归取复制所有层级
// 代理法 function deepClone(obj) { if (!isObject(obj)) { throw new Error('obj 不是一个对象!') } let isArray = Array.isArray(obj) let cloneObj = isArray ? [...obj] : { ...obj } Reflect.ownKeys(cloneObj).forEach(key => { cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key] }) return cloneObj }
4.手动递归赋值
let obj1 = { a: 1, b: 2 } let obj2 = { a: obj1.a, b: obj1.b } obj2.a = 3; alert(obj1.a); // 1 alert(obj2.a); // 3
5.用slice实现数组的深拷贝
// 当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝 // 当数组里面的值是引用数据类型,比如Object,Array时,属于浅拷贝 var arr1 = ["1","2","3"]; var arr2 = arr1.slice(0); arr2[1] = "9"; console.log("数组的原始值:" + arr1 );//1,2,3 console.log("数组的新值:" + arr2 );//1,9,3
6.用concat实现对数组的深拷贝
// 当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝 var arr1 = ["1","2","3"]; var arr2 = arr1.concat(); arr2[1] = "9"; console.log("数组的原始值:" + arr1 );//["1","2","3"] console.log("数组的新值:" + arr2 );//["1","9","3"] // 当数组里面的值是引用数据类型,比如Object,Array时,属于浅拷贝 var arr1 = [{a:1},{b:2},{c:3}]; var arr2 = arr1.concat(); arr2[0].a = "9"; console.log("数组的原始值:" + arr1[0].a ); // 数组的原始值:9 console.log("数组的新值:" + arr2[0].a ); // 数组的新值:9
7.直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果
function deepClone(initalObj, finalObj) { var obj = finalObj || {}; for (var i in initalObj) { var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况 if(prop === obj) { continue; } if (typeof prop === 'object') { obj[i] = (prop.constructor === Array) ? [] : Object.create(prop); } else { obj[i] = prop; } } return obj; }