普通类型拷贝
直接return型
Number String Boolean Symbol
对于这三种数据类型非引用类型的拷贝,直接return即可
undefined 与 null
为什么将这两者单独拿出来说呢?其实涉及到一个知识点,undefined与null在’=='状态下是相等的,而且该两种数据类型并没有constructor属性。
特殊对象类型
RegExp 与 Date
使用new关键字,传入对象即可。
Error
可以发现,当使用new关键字传入对象时,创建后的对象嵌套了一层
Error
对象,因此使用Error的message
属性。即:
Function
对于函数类型的对象,我们可以返回一个具名函数,并为其绑定this,并在具名函数中返回函数的返回值即可:
其中的参数问题在代码中解决。
普通对象类型与数组类型
递归每一项key,value即可。于是有下列代码:
const completeDeepClone = (target) => {
// 补全代码
// undefined与null
if (target == null) return target
// Date
if (target instanceof Date) return new Date(target)
// 正则
if (target instanceof RegExp) return new RegExp(target)
// Error
if (target instanceof Error) return new Error(target.message)
// 函数
if (target instanceof Function) return function proxy(...args) {
return target.call(this, args)
}
// Symbol Number String Boolean
if (typeof target !== 'object') return target
// 处理过undefined与null,该处就可直接创建新对象了
let newObj = new target.constructor()
for (let key in target) {
if (target.hasOwnProperty(key)) {
// 只剩对象,数组,Set,Map类型,递归引用类型的key value即可
newObj[key] = completeDeepClone(target[key])
}
}
return newObj
}
循环引用
其实到这里并没有完,那就是循环引用的问题。就是当待拷贝对象obj中的某个key的value值为obj时,以上的递归就会一直递归下去,成为死递归了。
最终方案
因此我们可以传入一个WeakMap
数据类型,简单介绍一个该数据类型,该数据类型的key必须为引用类型,具有增(add()),删(delete()),改(set()),查(has())四个基本方法,且为弱引用类型。可以最大程度上节省内存。至于详细内容,大家可以借鉴该篇博客JavaScript中的弱引用和强引用。
const _completeDeepClone = (target, hash = new WeakMap()) => {
// 补全代码
if (target == null) return target
if (target instanceof Date) return new Date(target)
if (target instanceof RegExp) return new RegExp(target)
if (target instanceof Error) return new Error(target.message)
if (target instanceof Function) return function proxy(...args) {
return target.call(this, args)
}
if (typeof target !== 'object') return target
if (hash.has(target)) return hash.get(target)
let newObj = new target.constructor()
hash.set(target, newObj)
for (let key in target) {
if (target.hasOwnProperty(key)) {
newObj[key] = _completeDeepClone(target[key], hash)
}
}
return newObj
}
思考
其实这种深拷贝方法还有两个缺陷:
- 对于不可枚举类型的数据结构,如WeakMap,WeakSet(),arguments类型的数据无法进行深拷贝。
- 如果待拷贝对象的key为Symbol类型时,该深拷贝方法也无法达到目的。(尚在思考中,待续)