深拷贝
b是a的一份拷贝,且b中没有对a中属性的引用
序列化
const b = JSON.parse(JSON.stringify(a))
缺点:JSON 不支持的类型(函数、Date、undefined、正则等)以及不支持自引用(a.self = a)
基本数据类型
首先根据数据类型区别基本数据类型和对象(instanceof Object
),基本数据类型则直接返回
if(v instanceof Object) {
// ...
} else {
return v
}
对象类型
对象类型使用 for in
递归深拷贝其内部自有属性
if(v instanceof Object) {
let result = undefined
// ...
// 对内部自有属性进行递归拷贝
for(let key in v){
if(v.hasOwnProperty(key)){
result[key] = deepClone(v[key])
}
}
return result
} else {
return v
}
原型
根据 value 的原型,初始化深拷贝结果 result
,相当于提前复制原型链上的属性
主要有 Array
、Function
、Date
、RegExp
和 Plain Object
let result = undefined
// Array
if(v instanceof Array) {
result = new Array()
// Function
} else if(v instanceof Function){
// 普通函数
if(v.prototype) {
result = function(){ return v.apply(this, arguments)}
// 箭头函数
} else {
result = (...args) => { return v.call(undefined, ...args)}
}
// Date
} else if(v instanceof Date) {
// Date的数据减0会转为时间戳,再利用这个时间戳构造新的Date
result = new Date(v - 0)
// RegExp
} else if(v instanceof RegExp) {
result = new RegExp(v.source, v.flags)
// Plain Object
} else {
result = new Object()
}
其中,函数要区别普通函数和箭头函数(v.prototype
);时间类型的数据减 0 会隐式转换为时间戳,利用这个时间戳构造新的 Date;尽可能使用 new
关键字来初始化 result
自引用
对于 a.self = a
的自引用情况,可以使用一个 Map
来记录拷贝的属性(key和value均为拷贝的值),如果发现自引用了,就直接返回a
// 利用Map对每一次拷贝做记录
const cache = new Map()
const deepClone = (v) => {
if(v instanceof Object) {
// 如果发现自引用(key 对象存在),直接返回 v
if(cache.get(v)){ return cache.get(v)}
// ...
// 将拷贝的值与结果存入map
cache.set(v, result)
// ...
} else {
return v
}
}
完整代码(附测试用例)
// 利用Map对每一次拷贝做记录
const cache = new Map()
const deepClone = (v) => {
if(v instanceof Object) {// object
// 如果发现自引用(key对象存在),直接返回v
if(cache.get(v)){ return cache.get(v)}
let result = undefined
if(v instanceof Array) {// object-Array
result = new Array()
} else if(v instanceof Function){// object-Function
if(v.prototype) {// 普通函数(都有prototype属性)
result = function(){ return v.apply(this, arguments)}
} else {// 箭头函数
result = (...args) => { return v.call(undefined, ...args)}
}
} else if(v instanceof Date) {// object-Date
// Date的数据减0会转为时间戳,再利用这个时间戳构造新的Date
result = new Date(v - 0)
} else if(v instanceof RegExp) {// object-RegExp
result = new RegExp(v.source, v.flags)
} else { // object-Object
result = new Object()
}
// 将拷贝的值与结果存入map
cache.set(v, result)
// 对内部自有属性进行递归拷贝
for(let key in v){
if(v.hasOwnProperty(key)){
result[key] = deepClone(v[key])
}
}
return result
} else {// 基本数据类型
return v
}
}
// 测试用例
const a = {
number:1, bool:false, str: 'hi', empty1: undefined, empty2: null,
array: [
{name: 'frank', age: 18},
{name: 'jacky', age: 19}
],
date: new Date(2000,0,1,20,30,0),
regex: /\.(j|t)sx/i,
obj: { name:'frank', age: 18},
f1: (a, b) => a + b,
f2: function(a, b) { return a + b }
}
// 自引用
a.self = a
const b = deepClone(a)
b.self === b // true
b.self = 'hi'
a.self !== 'hi' //true