平时总遇到这种问题:将对象obj赋值给变量a,然后改变了变量a中某个值,结果对象obj中对应的值也被改变了,酱紫就有些烦。数组arr也是此类问题。
然后百度了一下方法,看到一篇不错的博文,记下来当做笔记。
原文链接:
一、深拷贝两种方法:
1、方法一:JSON.stringify()和JSON.parse。(适用于简单的数据:undefined
、function
、symbol
会在转换过程中忽略)
let obj = {name:'old',id:1};
let newObj = obj;
newObj.name = 'new';
console.log('old:',obj); //old: {name: "new", id: 1}
console.log('new',newObj); //new {name: "new", id: 1}
let obj2 = {name:'old',id:1};
let newObj2 = JSON.parse(JSON.stringify(obj2));
newObj2.name = 'new';
console.log('old:',obj2); //old: {name: "old", id: 1}
console.log('new',newObj2); //new {name: "new", id: 1}
let arr1 = [0,1,2,3,4];
let newArr1 = arr1;
newArr1.push(8);
console.log(arr1); //[0, 1, 2, 3, 4, 8]
console.log(newArr1); //[0, 1, 2, 3, 4, 8]
let arr2 = [0,1,2,3,4];
let newArr2 = JSON.parse(JSON.stringify(arr2));
newArr2.push(8);
console.log(arr2); //[0, 1, 2, 3, 4]
console.log(newArr2); //[0, 1, 2, 3, 4, 8]
2、方法二:递归方法。(可以拷贝带有函数的对象)【以下代码复制的参考博文】
function deepClone(source){
const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
for(let keys in source){ // 遍历目标
if(source.hasOwnProperty(keys)){
if(source[keys] && typeof source[keys] === 'object'){ // 如果值是对象,就递归一下
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
}else{ // 如果不是,就直接赋值
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
const cloneObj = deepClone(originObj);
console.log(cloneObj === originObj); // false
cloneObj.a = 'aa';
cloneObj.c = [1,1,1];
cloneObj.d.dd = 'doubled';
console.log(cloneObj); // {a:'aa',b:'b',c:[1,1,1],d:{dd:'doubled'}};
console.log(originObj); // {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
带函数的:
const originObj = {
name:'axuebin',
sayHello:function(){
console.log('Hello World');
}
}
console.log(originObj); // {name: "axuebin", sayHello: ƒ}
const cloneObj = deepClone(originObj);
console.log(cloneObj); // {name: "axuebin", sayHello: ƒ}
二、JavaScript
中数组自带的拷贝方法 (首层浅拷贝)【以下代码复制的参考博文】
1、concat
数据只有一层,可以实现深拷贝:
const originArray = [1,2,3,4,5];
const cloneArray = originArray.concat();
console.log(cloneArray === originArray); // false
cloneArray.push(6); // [1,2,3,4,5,6]
console.log(originArray); [1,2,3,4,5];
但是,如果数据不止一层,第二层就又变成浅拷贝了:
const originArray = [1,[1,2,3],{a:1}];
const cloneArray = originArray.concat();
console.log(cloneArray === originArray); // false
cloneArray[1].push(4);
cloneArray[2].a = 2;
console.log(originArray); // [1,[1,2,3,4],{a:2}]
2、slice
多层数据拷贝结果:
const originArray = [1,[1,2,3],{a:1}];
const cloneArray = originArray.slice();
console.log(cloneArray === originArray); // false
cloneArray[1].push(4);
cloneArray[2].a = 2;
console.log(originArray); // [1,[1,2,3,4],{a:2}]
结果:concat和slice都一样,都是首层浅拷贝,只能深拷贝数据结构简单,只有一层的数据。
三、ES6 中的拷贝方法 (首层浅拷贝)【以下代码复制的参考博文】
1、Object.assign()
Object.assign()
拷贝的是属性值。假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值。
2、...展开运算符
const originArray = [1,2,3,4,5,[6,7,8]];
const originObj = {a:1,b:{bb:1}};
const cloneArray = [...originArray];
cloneArray[0] = 0;
cloneArray[5].push(9);
console.log(originArray); // [1,2,3,4,5,[6,7,8,9]]
const cloneObj = {...originObj};
cloneObj.a = 2;
cloneObj.b.bb = 2;
console.log(originObj); // {a:1,b:{bb:2}}
结果:Object.assign()
和...展开运算符都一样,也是首层浅拷贝。
四、首层浅拷贝【以下代码复制的参考博文】
就是对目标对象的第一层进行深拷贝,然后后面的是浅拷贝,可以称作“首层浅拷贝”。(理论和概念性的东西不太擅长,也直接引用博文中的吧)
例子:
function shallowClone(source) {
const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
for (let keys in source) { // 遍历目标
if (source.hasOwnProperty(keys)) {
targetObj[keys] = source[keys]; //直接=号赋值
}
}
return targetObj;
}
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
const cloneObj = shallowClone(originObj);
console.log(cloneObj === originObj); // false
cloneObj.a='aa';
cloneObj.c=[1,1,1];
cloneObj.d.dd='surprise';
console.log(cloneObj);
// {a:'aa',b:'b',c:[1,1,1],d:{dd:'surprise'}}
console.log(originObj);
// {a:'a',b:'b',c:[1,2,3],d:{dd:'surprise'}}
因为在方法中,第一层数据进行了深拷贝,第二层却用了=号直接赋值,所以原先的第一层数据不受影响,但是第二层开始的数据还是会受影响而发生改变。
总结:1、只有递归能完全进行深拷贝;
2、简单的数据结构可以用JSON.parse和JSON.stringify来进行深拷贝;
3、concat、slice、Object.assign、...展开运算符都只是首层浅拷贝;