浅拷贝and深拷
携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天 [携手创作,共同成长]
相信大家不管是在日常的开发过程中,还是我们在找工作面试的时候,都会有提及到这个概念,今天就我们一起深入了解一下
首先在这里我们要知道一个概念就是js的主要的数据类型有哪些:
值类型(基本类型) :字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol。
引用数据类型(对象类型) :对象(Object)、数组(Array)、函数(Function),还有两个特殊的对象:正则(RegExp)和日期(Date)。
基本类型是按值访问的,引用类型是按照引用访问的
基本类型和引用类型也有人叫原始类型和对象类型,拥有方法的类型和不能拥有方法的类型,可变类型和不可边类型
深拷贝和浅拷贝
首先我们看一个关系图:
如图所示:
obj2是对obj1的浅拷贝,obj2对象赋值的是obj1对象的指针,也就是内存地址,这个时候他们两个是同时指向一个内存的也就是当我修改obj1的值的时候,obj2也是会改变的,同理修改obj2也是一样的。
obj3是对obj1的深拷贝,他有自己的一个内存地址,在修改obj3的时候是不会影响obj1的数据的,obj3和obj1是不共享内存的
得出的结论: 深拷贝:是新建一个一模一样的对象,该对象与原对象不共享内存,修改新对象也不会影响原对象
浅拷贝:只是复制某个对象的指针,就是新建一个对象,但是这个对象只是复制了另一个对象的指针,这两个对象是公用一个内存块的
深拷贝与浅拷贝实现(代码层次)
既然已经知道了深拷贝与浅拷贝的由来,那么该如何实现深拷贝?我们先看看array和object自有的方法:
Array
对于数组我们可以使用slice()和concat()方法来解决上面的问题
slice方法实现简单数组的深拷贝
concat方法实现简单数组的深拷贝
var arr1=[1,2];
arr2=arr1.slice();
arr1[0]=3
console.log(arr2);
var arr1=[1,2];
arr2=arr1.concat();
arr1[0]=3
console.log(arr2);
此时,arr1的修改并没有影响到arr2,这就是简单数组的深拷贝
将元素改成引用数据类型
var arr3 = [ {a:1} ];
arr4 = arr3.slice();
arr3[0].a = 'arr3';
console.log(arr3,arr4); // 返回新数组
var obj5 = { a:2 };
var obj6 = Object.create(obj5);
obj5.a = 'obj5';
console.log(obj5 === obj6); // false
console.log(obj5.a === obj6.a); // true
我们看完上面的操作,只能实现新数组新对象的返回,只能形成第一层的深拷贝,到这里我们可以综合上述的基本思想,自己封装一个方法来实现对象的深拷贝,代码如下:
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);
\