深拷贝
概念
深拷贝是将一个对象从内存中完整的拷贝一份,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。新对象跟原对象不共享内存
举例
const obj1 = {
name: "sun",
age: 18,
favour: ["ball", "games", "study"],
birthCity: {
Province: "四川"
}
}
// 深拷贝
const obj2 = deepClone(obj1)
function deepClone(params) {
// 深拷贝方法,后续会实现
}
obj2.favour[0] = "a"
console.info(obj1) // => obj1.favour = ["ball", "games", "study"]
实现
- 对于最简单的实现
JSON.parse(JSON.stringify(obj))
// 存在很大的弊端,只能拷贝纯对象,数组,不能拷贝正则,日期对象等,无法处理循环引用问题
// 循环引用,即obj.a = obj
- 简易版
// 使用递归深拷贝,但无法解决循环引用问题
const deepClone = target => {
if (typeof target === 'object' && target !== null) {
const cloneTarget = Array.isArray(target) ? [] : {}
for (let prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = deepClone(target[prop])
}
}
return cloneTarget
} else {
return target
}
}
- 解决循环引用版深拷贝
const isObject = target => (typeof target === "object" || typeof target === "function") && target !== null
const deepClone = (target, map = new weakMap()) => {
if (map.get(target)) return target
if (isObject(target)) {
map.set(target, true)
const cloneTarget = Array.isArray(target) ? [] : {}
for (let prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = deepClone(target[prop], map)
}
}
return cloneTarget
} else {
return target
}
}
const a = {value: 1}
a.target = a
let newA = deepClone(a)
console.info(newA)//{ val: 2, target: { val: 2, target: [Circular] } }
- 完整的深拷贝
// 考虑到了多种情况
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;
}