本文主要总结了关于深拷贝的三种实现方式
JSON.parse 和 JSON.stringify
- 缺点
- 函数、特殊对象、Symbol等,若为数组中的值,会变成null;非数组,会变成undefined
- 循环引用报错
- 对象原型被改变
递归
- 解决的问题
- 特殊对象类型的深拷贝:Number,String,Boolean,Map,Set,Date
- 对象原型保持原来的
- 循环引用和同级引用问题
- 对象key为Symbol时,遍历出来的键值问题
- 缺点
- 递归容易爆栈
const checkType = (target) => {
return Object.prototype.toString.call(target).slice(8, -1)
}
const clone = (target) => {
const map = new WeakMap() // 解决引用问题
const _clone = (target) => {
if (target === null) return null
if (typeof target !== 'object') return target
const type = checkType(target)
let result
const constructor = target.constructor
switch (type) {
// 处理特殊对象
case 'String':
case 'Number':
case 'Boolean':
case 'Set':
case 'Map':
result = new constructor(target)
break
// 处理Date
case 'Date':
result = new Date(target.getTime())
break
// 处理数组
case 'Array':
result = []
break
// 处理对象
case 'Object':
const proto = Object.getPrototypeOf(target)
result = Object.create(proto)
break
default:
result = target
}
// 解决引用问题
const temp = map.get(target)
if (temp) return temp
map.set(target, result)
// 遍历对象属性(考虑到属性为Symbol的时候)
const keys = Reflect.ownKeys(target)
for (const key of keys) {
result[key] = _clone(target[key])
}
return result
}
return _clone(target)
}
循环
- 解决的问题
- 不会产生递归爆栈问题
const checkType = (target) => {
return Object.prototype.toString.call(target).slice(8, -1)
}
const initType = (target) => {
const type = checkType(target)
let result
switch (type) {
case 'Number':
case 'String':
case 'Boolean':
case 'RegExp':
case 'Set':
case 'Map':
const constructor = target.constructor
result = new constructor(target)
break;
case 'Date':
result = new Date(target.getTime())
break
case 'Array':
result = []
break
case 'Object':
const proto = Object.getPrototypeOf(target)
result = Object.create(proto)
break
default:
result = target
break;
}
return result
}
const cloneLoop = (x) => {
if (x === null) return null
if (typeof x !== 'object') return x
const root = initType(x)
const map = new WeakMap()
// 栈
const loopList = [
{
parent: root, // 要复制的元素
key: undefined, // 键值
data: x // 键值对应的数据
}
]
while (loopList.length) {
const node = loopList.pop()
const { parent, key, data } = node
let res = parent
if (typeof key !== 'undefined') {
res = parent[key] = initType(data)
}
const cache = map.get(data)
if (cache) {
parent[key] = cache
continue
}
map.set(data, res)
for (const k of Reflect.ownKeys(data)) {
if (data[k] === null || typeof data[k] !== 'object') {
res[k] = data[k]
} else {
loopList.push({
parent: res,
key: k,
data: data[k]
})
}
}
}
return root
}