目录
1.什么是深浅拷贝?
了解深浅拷贝之前必须要先知道js数据类型和存储方式(基本数据类型储存在栈内存,引用类型储存在堆内存中)。
1.1 浅拷贝
是指对内存地址的复制,让目标对象指针和源对象指向同一片内存空间。
例子:
<script> var obj={a:1,b:2} var tempobj=obj console.log(obj)//{a:1,b:2} console.log(tempobj)//{a:1,b:2} obj.a=111 console.log(obj.a)//111 console.log(tempobj.a)//111 tempobj=obj 就属于浅拷贝 </script>
1.2 深拷贝
是指拷贝对象的具体内容,二者内存地址是自主分配的,拷贝结束之后俩个对象虽然存的值是一样的,但是内存地址不一样,俩个对象页互相不影响,互不干涉。
总而言之:假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着 变了,说明这是浅拷贝,如果B没变,那就是深拷贝;
2.如何实现深拷贝?
Array:
(1)sclice方法:
<script>
var arr1=[1,2,3]
arr2 = arr1.slice();
arr1[0]=111
console.log(arr1) //[111,2,3]
console.log(arr2)//[1,2,3]
</script>
(2)concat
<script>
var arr10 = [1, 2];
var arr20 = arr10.concat();
arr10[0] = "arr1";
console.log("============");
console.log(arr10);//['arr1', 2]
console.log(arr20);//[1, 2]
</script>
数据变为引用类型:
<script>
var arr3 = [{ a: 1 }];
arr4 = arr3.slice();
arr3[0].a = "arr3";
console.log(arr3, arr4); // [{ a: arr3 }] [{ a: arr3 }]返回新数组
var obj5 = { a: 2 };
var obj6 = Object.create(obj5);
obj5.a = "obj5";
console.log(obj5 === obj6); // false
console.log(obj5.a === obj6.a); // true
</script>
引用类型的时slice concat 都不能深拷贝了,只能实现第一层的深拷贝。
① JSON内置的方法:
<script>
var a = { x: 1 };
var b = JSON.parse(JSON.stringfiy(a));
console.log(b); //{x:1}
b.x = 2;
console.log(b); //{x:2}
console.log(a); //{x:1}
</script>
该方法是用JSON.parse将对象转为字符串,然后在用JSON.stringify转回对象json字符串转换为对象的时候,会自己去构建新的内存地址存放数据
注:如果对象属性为function,因为JSON格式字符串不支持function,在构建的时候会自动删除
②Object的内置方法assign:
<script>
var a = { x: 1 };
var b = Object.assign({}, a);
console.log(b); //{x:1}
b.x = 2;
console.log(b); //{x:2}
console.log(a); //{x:1}
</script>
该方法是用Object.assign对对象进行拼接, 将后续对象的内容插入到第一个参数指定的对象,不会修改第一个参数之后的对象,而我们将第一个对象指定为一个匿名空对象,实现深拷贝
注:对象嵌套层次过深,超过2层,就会出现浅拷贝的状况,比如echarts组件的option对象
3.深拷贝工具
3.1第三方库:lodash under-score
3.2自己封装:
let obj1 = {
// typeof 数组|对象 => 'object'
// Array.isArray()
arr: [1, 2, 3],
arrayOfObjs: [{ c: 5 }, { d: 6 }],
date: new Date(),
object: { val: 4 },
// 增加一个属性
fn: function () {
// typeof fn ===> 'function'
return 5;
},
set: new Set([7, 8, 9, { e: 10 }]),
map: new Map([
[11, "f"],
[12, "g"],
]),
reg: /[h-z]/,
};
console.log(obj1);
var temp1 = JSON.parse(JSON.stringify(obj1));
console.log(obj1);
console.log(temp1);
obj1.arr[0] = "obj修改了";
obj1.arrayOfObjs[0] = "111";
console.log(obj1);
console.log(temp1);
由此可分不同情况进行逻辑封装:
let obj1 = {
// typeof 数组|对象 => 'object'
// Array.isArray()
arr: [1, 2, 3],
arrayOfObjs: [{ c: 5 }, { d: 6 }],
date: new Date(),
object: { val: 4 },
// 增加一个属性
fn: function () {
// typeof fn ===> 'function'
return 5;
},
set: new Set([7, 8, 9, { e: 10 }]),
map: new Map([
[11, "f"],
[12, "g"],
]),
reg: /[h-z]/,
};
function deepClone(obj) {
if (obj === null || typeof obj !== "object") {
return obj;
}
// 预防环形对象的处理
if (!deepClone.cached) {
deepClone.cached = new WeakMap(); // 虚引用map
}
// 如果obj已经保存过,无需执行后续
if (deepClone.cached.has(obj)) {
return deepClone.cached.get(obj);
}
// 几大类型
if (obj instanceof Map) {
// 新建一个map 替代老Map
let tmp = new Map();
// 保存
deepClone.cached.set(obj, tmp);
for (let [key, value] of obj) {
// of 取值 in是取key
tmp.set(key, deepClone(value)); // value可能是引用数据类型,需要处理!!!!!
}
return tmp;
} else if (obj instanceof Set) {
// 结构类似[]
let tmp = new Set();
deepClone.cached.set(obj, tmp);
for (let val of obj) {
tmp.add(deepClone(val)); // value可能是引用数据类型,需要处理!!!!!
}
return tmp;
} else if (obj instanceof RegExp) {
let tmp = new RegExp(obj);
deepClone.cached.set(obj, tmp);
return tmp;
} else {
// 数组 + 对象
// obj // 数组 || 对象
// 1: 创建一个新的对象 或者 数组 new [???]
let tmp = new obj.constructor(); // Array Object Date
deepClone.cached.set(obj, tmp);
for (let key in obj) {
tmp[key] = deepClone(obj[key]); // value可能是引用数据类型,需要处理!!!!!
}
return tmp;
}
}
let obj2 = {
toObj: obj1,
};
obj1.toObj = obj2;
// obj1 => obj2 => obj1 => obj2 => obj1 => obj2 => obj1 环形对象
// JSON.parse(JSON.stringify(obj1)) 报错
let cloned = deepClone(obj1);
console.log(obj1)
console.log(obj2)