深拷贝 浅拷贝
前言
大家对于拷贝这个词都不陌生吧,顾名思义就是copy,拷贝分为两种——深拷贝和浅拷贝,实现这两种拷贝有很多种方式,本文来对它们做个总结。
正文
一、定义
浅拷贝:复制
内存地址指针的操作,拷贝得到的对象会受原对象的影响。
PS:(原始值类型不存在浅拷贝,只有引用类型有浅拷贝)
深拷贝:创建一个
新的对象来承接原对象中的原始值,拷贝得到的对象不会受原对象的影响。
这里补充一个小小的知识点:js中数据类型分为基本数据类型和引用数据类型。
- 基本数据类型:Number,String,Boolean,null,undefined,Symbol(ES6新增,表示
独一无二的值),Bigint(比Number数据类型支持的范围更大的整数值,整数溢出不是问题) - 引用数据类型:Object,Array,Function,Date
二、实现
浅拷贝的实现
- 引用类型的赋值
let a={name:'aa'} //对象为引用类型,放在堆里面
let b=a //调用栈里a,b共享堆中对象的地址
a.name = 'bb' //修改堆中对象的name的值
console.log(b.name); //bb
- Object.create(a)
let a={name:'aa'}
let b=Object.create(a) //创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)
a.name = 'bb' //修改堆中对象的name的值
console.log(b.name); //bb
- arr.concat( ) 拼接数组
- 如果数组中都是基本数据类型,可以说是深拷贝
let arr=['old',1,true,null,undefined]
let newArr=arr.concat() //拼接了个空气进去,返回一个新数组
arr[0]='new'
console.log(newArr); //[ 'old', 1, true, null, undefined ]
- 如果数组中有引用数据类型,那就是浅拷贝了
let arr=[{n:'old'},1,true,null,undefined]
let newArr=arr.concat()
arr[0].n='new'
console.log(newArr); //[ { n: 'new' }, 1, true, null, undefined ]
- arr.slice( ) 切割数组
let arr=[{n:'old'},1,true,null,undefined]
let newArr=arr.slice() //截取所有
arr[0].n='new'
console.log(newArr); //[ { n: 'new' }, 1, true, null, undefined ]
深拷贝的实现
JSON.parse(JSON.stringify(obj)):先将对象转为字符串,再转回对象
let arr=[{n:'old'},1,true,null,undefined]
let newArr=JSON.parse(JSON.stringify(arr))
arr[0].n='new'
console.log(newArr);//[ { n: 'old' }, 1, true, null, null ]
But,这种方法也有一定的局限:
- 不能拷贝undefined
- 不能拷贝函数
- 无法处理循环引用的情况
- 不能拷贝Symbol类型
重点来了!面试官:手写一个深拷贝
思路:
-
将要拷贝对象的所有属性copy到新的对象中。对于
原始值数据类型和function不用特殊处理,因为函数没有子集,只需要处理Object对象的引用类型。 -
对于
Object的引用数据类型可以分为对象和数组,需要分别处理。 -
深拷贝的对象的
引用类型的属性中仍可能包含引用类型,这里可以用递归来实现
实现:
let obj = {
name: 'zt_ever',
age: 18,
like: {
type: 'coding',
num:{
n:2
}
}
}
function deepCopy(obj){
if (typeof obj !== 'object' || obj === null) return //obj类型不为对象或为空
let newObj = obj instanceof Array ? [] : {} //obj类型为数组还是对象
for(let key in obj){ //遍历对象的所有键名
if(typeof obj[key]==='object'&&obj[key]!==null){ //obj[key]是对象且不为空
//创建一个新的对象
newObj[key]=deepCopy(obj[key]) //递归 创建的新对象赋给newObj[key]
}
else{
newObj[key]=obj[key]//将键值赋给newObj
}
}
return newObj
}
let newObj = deepCopy(obj)
console.log(newObj); //{ name: 'zt_ever', age: 18, like: { type: 'coding', num: { n: 2 } } }
obj.age=20 //修改对象的值
obj.like.num.n=3
console.log(newObj); //{ name: 'zt_ever', age: 18, like: { type: 'coding', num: { n: 2 } } }
总结
以上就是我对于深拷贝和浅拷贝的个人总结,如有错误欢迎评论指出~