深/浅拷贝?
const a = [1,2,3]
let b = a
b[1] = 5
console.log(b) //[1, 5, 3]
console.log(a) //[1, 5, 3]
通俗的说:
a赋给了b,改b a也变 改a b也变,这就是浅拷贝(上边的例子)。
a赋给了b,改b a不变 改a b不变,这就是深拷贝。
稍微专业点的说:
- 浅拷贝:只是拷贝了源对象的
地址,所以源对象的任何值发生改变时,拷贝对象的值也会随之而发生变化。 - 深拷贝:则是拷贝了源对象的所有
值而不是地址,所以即使源对象的值发生任何变化时,拷贝对象的值也不会改变。
深拷贝的两种方式
1. JSON
首先JSON.stringify() 方法将一个 JavaScript 对象或值转换为 JSON 字符串,
然后JSON.parse() 方法用来解析JSON字符串,构造由字符串描述的JavaScript值或对象。
const b = JSON.parse(JSON.stringify(a))
没错,这样就实现了深拷贝,但是有缺陷。 JSON不支持日期正则和函数等等...
还不支持引用。如果a包含了对其他对象的引用,可能会报错。
2. 递归
程序员何苦为难程序员,也许少写两行代码能少掉两根头发。
要点:递归、判断类型、检查环、不拷贝原型上的属性
首先让我们先不带任何顾虑的写几行代码...
const deepClone = (a)=>{
//判断传进来的a是不是对象
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 ){
//a-0会变成一串数字 当时间戳用
result = new Date(a-0)
}else if(a instanceof RegExp ){
//接受两个参数 一个文本 一个flags
result = new RegExp(a.source,a.flags)
}else{ //其他的都是普通对象
result = { }
}
for(let key in a){
result[key] = deepClone(a[key]) //递归
}
return result
}
}else{ //基本数据类型
return a
}
}
真不错,但是还有问题。
如果加一句 a.self = a 去看a的话会看到a.self 看到 a.self又看到a 自己引用自己 那递归就没有出口了。
怎么解决?
把之前拷过的东西都记下来 再遇见就直接return 用Map记录(因为map的key可以是对象)
const cache = new Map()
const deepClone = (a)=>{
if(cache.get(a)) { //新加代码
//如果a已经存在cache里了 就说明已经完事了
return cache.get(a)
}
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 = { }
}
cache.set(a,result) //新加代码
for(let key in a){
result[key] = deepClone(a[key]) //递归
}
return result
}
}else{ //基本数据类型
return a
}
}
还有一个问题。我们在遍历a的时候没必要什么都遍历,a有些属性是继承得到的,继承得到的没必要拷贝它。
只需要在遍历的时候加个判断if(a.hasOwnProperty(key) )
const cache = new Map()
const deepClone = (a)=>{
if(cache.get(a)) {
//如果a已经存在cache里了 就说明已经完事了
return cache.get(a)
}
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 = { }
}
cache.set(a,result)
for(let key in a){
if(a.hasOwnProperty(key) ){ //新加代码
result[key] = deepClone(a[key])
}
}
return result
}
}else{ //基本数据类型
return a
}
}
打完收工。