- 初写:使用递归、判断类型(注意看注释内容,写得很仔细清楚)
const deepClone=(a)=>{
//判断a的类型,是否为对象object
if(a instanceof Object){
let result=undefined //先预设好返回值
//object
//再分是哪种类,函数、数组、日期等
if(a instanceof Function){ //判断是否为函数
//判断为普通函数还是箭头函数
if(a.prototype){ //判断是否为普通函数
result=function(){return a.apply(this,arguments)} //返回的函数要和a函数功能一模一样(该函数接收什么参数,就一样的传给a(a.apply(this,arguments)),然后将a的返回值作为该函数的返回值,即该函数为a函数的深拷贝)
}else{ //不是普通函数,就是箭头函数了(仅讨论这两种常见函数)
result=(...args)=>{return a.call(undefined,...args)} //同样也是将相同参数传给a,然后将a的返回值作为该函数的返回值
}
}else if(a instanceof Array){ //判断是否为数组
result=[] //那该结果就是一个数组,内容为下方的遍历
}else if(a instanceof Date){ //判断是否为日期
result=new Date(a-0) //使用的是时间戳
}else if(a instanceof RegExp){ //还有可能是正则
result=new RegExp(a.source,a.flags) //正则的属性source和flags
}else{ //其他为普通对象
result={}
}
//在此之前的'result='是在构造对应的本体,下方的遍历是内容
for(let key in a){ //遍历a里所有的属性,然后将所有属性再进行一次深拷贝
result[key]=deepClone(a[key]) //递归
}
return result
}else{
//string number bool null undefined symbol bigint
return a //普通的数据类型就不用构造了
}
}
- 验证:有一个复杂的a对象,然后b深拷贝a
const a={
number:1,bool:false,str:'hi',empty1:undefined,empty2:null,
array:[
{name:'frank',age:18},
{name:'jack',age:19}
],
date:new Date(2024-06-03),
regexp:/\.(j|t)sx/i,
obj:{name:'frank',age:18},
f1:(a,b)=>a+b,
f2:function(a,b){return a+b}
}
const b=deepClone(a)
- 深拷贝成功,a和b互不影响
- 检查环
- 当加上'a.self=a'(自己引用自己,即指向当前a对象的引用)后,递归没有出口
- 如何解决该环:用一个Map记录,当要再次深拷贝a时,直接return b即可,如图
const cache=new Map() //为什么用Map,因为key是对象
const deepClone=(a)=>{
//判断是否已经深拷贝过了,即看缓存cache里有没有记录
if(cache.get(a)){
return cache.get(a) //若有,直接return a(即拒绝该深拷贝) ,其实是b哦,因为已进行过深拷贝了
}
if(a instanceof Object){
let result=undefined
if(a instanceof Function){
if(a.prototype){
result=function(){return a.apply(this,arguments)}
}else{
result=(...args)=>{return a.call(undefined,...args)}
}
}else if(a instanceof Array){
result=[]
}else if(a instanceof Date){
result=new Date(a-0)
}else if(a instanceof RegExp){
result=new RegExp(a.source,a.flags)
}else{
result={}
}
//result构造出来后,将深拷贝记录进缓存里
cache.set(a,result)
for(let key in a){
result[key]=deepClone(a[key])
}
return result
}else{
return a
}
}
3. 注意:不要拷贝原型上的属性:有些属性是继承得到的,就不需要深拷贝到b身上(因为这些属性就不在a对象身上),所以要进行判断
for(let key in a){
if(a.hasOwnProperty(key)){ //若该key是在a身上,才会去拷贝,在原型上就不用拷贝
result[key]=deepClone(a[key])
}
}