导读
- 需要先了解数据类型
- 能区分浅拷贝与深拷贝
最常见的深拷贝
JSON.parse(JSON.stringify());
复制代码
实现深拷贝
- 递归
- 根据不同类型做对应的处理
- 处理循环引用
// 深拷贝
function deepCopy(ori, hash) {
const type = getType(ori);
switch (type) {
case 'object': return copyObject(ori, hash);
case 'array': return copyArray(ori, hash);
case 'date': return copyDate(ori);
case 'regExp': return copyRegExp(ori);
case 'element': return copyElement(ori);
default: return ori;
};
};
// 获取类型
function getType(obj) {
const str = Object.prototype.toString.call(obj);
const map = {
'[object String]': 'string',
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object Undefined]': 'undefined',
'[object Null]': 'null',
'[object Symbol]': 'symbol',
'[object Object]': 'object',
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regExp',
};
if (obj instanceof Element) {
return 'element';
};
return map[str];
};
// 复制对象
function copyObject(ori, hash = new WeakMap()) {
let copy = {};
// hash 作用是为了处理循环引用
if (hash.get(ori)) {
return hash.get(ori);
};
hash.set(ori, copy);
for (const [key, value] of Object.entries(ori)) {
copy[key] = deepCopy(value, hash);
};
return copy;
};
// 复制数组
function copyArray(ori, hash = new WeakMap()) {
let copy = [];
if (hash.get(ori)) {
return hash.get(ori);
};
hash.set(ori, copy);
for (const [key, value] of Object.entries(ori)) {
copy[key] = deepCopy(value, hash);
};
return copy;
};
// 复制日期
function copyDate(ori) {
return new Date(ori);
};
// 复制正则
function copyRegExp(ori) {
return new RegExp(ori);
};
// 复制元素
function copyElement(ori) {
return ori.cloneNode(true);
};
// 测试案例
const obj1 = {
name: 'kevin',
age: 30,
address: {
city: 'guangzhou'
},
arr: ['a', 'b', 'c'],
brr: [],
birth: new Date('1990-07-22'),
sy1: Symbol("KK"),
sy2: Symbol("KK"),
};
// 处理循环引用
obj1.key = obj1;
obj1.brr[0] = obj1;
const obj2 = deepCopy(obj1);
obj2.age = 20;
obj2.address.city = 'shanghai';
obj2.arr[0] = 'a1';
obj2.birth = new Date();
console.log(obj2.sy1 === obj2.sy2)
console.log(obj1);
console.log(obj2);
复制代码
深拷贝思路步骤
- 对象浅拷贝
function clone(target) {
let cloneTarget = {};
for (const key in target) {
cloneTarget[key] = target[key];
}
return cloneTarget;
};
复制代码
创建一个新的对象,遍历需要克隆的对象,将需要克隆对象的属性依次添加到新对象上,返回。
- 递归
function clone(target) {
if (typeof target === 'object') {
let cloneTarget = {};
for (const key in target) {
cloneTarget[key] = clone(target[key]);
}
return cloneTarget;
} else {
return target;
}
};
复制代码
- 简单区分对象数组,其他类型就不在这里扩展了
function clone(target) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {};
for (const key in target) {
cloneTarget[key] = clone(target[key]);
}
return cloneTarget;
} else {
return target;
}
};
复制代码
- 处理循环引用
function clone(target, map = new Map()) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {};
if (map.get(target)) {
return map.get(target);
}
map.set(target, cloneTarget);
for (const key in target) {
cloneTarget[key] = clone(target[key], map);
}
return cloneTarget;
} else {
return target;
}
};
复制代码
小结
希望看完本篇文章能对你有如下帮助:
- 其实在日常工作中,只要前后端协商好数据类型的定义,一般情况下使用JSON.parse(JSON.stringify());就可以了。
- 实现深拷贝,更多的是为了巩固实现过程的各个要点, 文中如有错误,欢迎在评论区指正,如果这篇文章帮助到了你,欢迎点赞和关注。