深拷贝
JSON.parse(JSON.stringify(object)) 缺点:
- 忽略undefined,function,Symbol
- 循环引用报错
- Date转换字符串,解析不正确
- 无法处理正则,会自动变成空对象
测试对象
const target = {
field1: 1,
field2: undefined,
field3: {
child: 'child'
},
field4: [2, 4, 8],
empty: null,
map,
set,
bool: new Boolean(true),
num: new Number(2),
str: new String(2),
symbol: Object(Symbol(1)),
date: new Date(),
reg: /\d+/,
error: new Error(),
func1: ()=>{
console.log('code秘密花园');
}
,
func2: function(a, b) {
return a + b;
}
};
初级版本
思路:先实现一个简单的浅拷贝,使用for...in循环属性并赋值
function clone(target){
let cloneTarget = {}
for(let key in target){
cloneTarget[key] = target[key]
}
return cloneTarget
}
接着,使用递归方式,进行对象的深度克隆
function clone(target) {
if (typeof target === 'object'&&target!==null) {
let cloneTarget = {}
for (let key in target) {
cloneTarget[key] = clone(target[key])
}
return cloneTarget
} else {
return target
}
}
考虑数组版本
思路:只需给target加上一个判断
function clone(target) {
if(typeof target!=='object'||target===null)return target
let cloneTarget = new Map
for(let key in target){
cloneTarget[key] = clone(target[key])
}
return cloneTarget
}
解决循坏引用
思路:使用 new Map 存储cloneTarget和target的映射关系
function clone(target) {
if(typeof target!=='object'||target===null)return target
let cloneTarget = Array.isArray(target)?[]:{}
for(let key in target){
cloneTarget[key] = clone(target[key])
}
return cloneTarget
}
完整版
const getType = obj => Object.prototype.toString.call(obj);
const isObject = (target) => (typeof target === 'object' || typeof target === 'function') && target !== null;
const canTraverse = {
'[object Map]': true,
'[object Set]': true,
'[object Array]': true,
'[object Object]': true,
'[object Arguments]': true,
};
const mapTag = '[object Map]';
const setTag = '[object Set]';
const boolTag = '[object Boolean]';
const numberTag = '[object Number]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const dateTag = '[object Date]';
const errorTag = '[object Error]';
const regexpTag = '[object RegExp]';
const funcTag = '[object Function]';
const handleRegExp = (target) => {
const { source, flags } = target;
return new target.constructor(source, flags);
}
const handleFunc = (func) => {
// 箭头函数直接返回自身
if(!func.prototype) return func;
const bodyReg = /(?<={)(.|\n)+(?=})/m;
const paramReg = /(?<=\().+(?=\)\s+{)/;
const funcString = func.toString();
// 分别匹配 函数参数 和 函数体
const param = paramReg.exec(funcString);
const body = bodyReg.exec(funcString);
if(!body) return null;
if (param) {
const paramArr = param[0].split(',');
return new Function(...paramArr, body[0]);
} else {
return new Function(body[0]);
}
}
const handleNotTraverse = (target, tag) => {
const Ctor = target.constructor;
switch(tag) {
case boolTag:
return new Object(Boolean.prototype.valueOf.call(target));
case numberTag:
return new Object(Number.prototype.valueOf.call(target));
case stringTag:
return new Object(String.prototype.valueOf.call(target));
case symbolTag:
return new Object(Symbol.prototype.valueOf.call(target));
case errorTag:
case dateTag:
return new Ctor(target);
case regexpTag:
return handleRegExp(target);
case funcTag:
return handleFunc(target);
default:
return new Ctor(target);
}
}
const deepClone = (target, map = new WeakMap()) => {
if(!isObject(target))
return target;
let type = getType(target);
let cloneTarget;
if(!canTraverse[type]) {
// 处理不能遍历的对象
return handleNotTraverse(target, type);
}else {
// 这波操作相当关键,可以保证对象的原型不丢失!
let ctor = target.constructor;
cloneTarget = new ctor();
}
if(map.get(target))
return target;
map.set(target, true);
if(type === mapTag) {
//处理Map
target.forEach((item, key) => {
cloneTarget.set(deepClone(key, map), deepClone(item, map));
})
}
if(type === setTag) {
//处理Set
target.forEach(item => {
cloneTarget.add(deepClone(item, map));
})
}
// 处理数组和对象
for (let prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = deepClone(target[prop], map);
}
}
return cloneTarget;
}
参考: