赋值、深/浅拷贝
概念
赋值:当我们吧一个对象赋值给一个新的变量时,赋的其实是该对象在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,其中任意一个发生变化,改动的都是同一个空间里面的内容
let ojb1 = {name:'web',age:11,likes:['run','sing']}
let obj2 = obj1
obj2.name = 'ken'
obj2.links[0] = 'runing'
console.log(obj1.name,obj1.links) // ken,['runing','sing']
浅拷贝:重新在堆中创建内存空间,拷贝前后的基本数据类型互不影响,但是对象会相影响,因为拷贝前后对象里面的引用类型(对象)都是指向同一地址(跟赋值一样)。
let obj3 = {name:'清咔',likes:['run','read']}
let obj4 = {...obj3}
obj4.name = '沐夕花开'
obj4.likes[0] = 'coding'
console.log(obj4.name,obj4.likes) // 沐夕花开,['coding','read']
深拷贝:重新在堆中创建内存空间,拷贝前后所有数据类型都互不影响,因为会递归对对象拷贝。
let obj5 = {name:'清咔',likes:['run','read']}
let obj6 = DeepClone(obj5)
function DeepClone(obj){
if(obj === null) return {}
if(obj instanceof Date) return new Date(obj)
if(obj instanceof RegExp) return new RegExp(obj)
if(typeof obj !== 'object') return obj
let _obj = new obj.constructor()
for(let key in obj){
if(obj.hasOwnProperty(key)){
_obj[key] = DeepClone(obj[key])
}
}
return _obj
}
obj6.name = '沐夕花开'
obj6.likes[0] = 'coding'
console.log(obj5.name,obj5.likes) // 清咔,['run','read']
| 操作 | 是否指向同一对象 | 第一层数据为基本数据类型 | 子对象 |
|---|---|---|---|
| 赋值 | 是 | 源数据也会改变 | 源数据也会改变 |
| 浅拷贝 | 否 | 源数据不会改变 | 源数据也会改变 |
| 深拷贝 | 否 | 源数据不会改变 | 源数据不会改变 |
实现
浅拷贝
- Object.assign()
- ES6展开运算符
- Array.prototype.concat()
- Array.prototype.slice()
深拷贝
-
JSON.parse(JSON.stringify())
利用JSON.stringify 将对象转换成JSON字符串,再通过JSON.parse()把字符串解析成对象来实现深拷贝,
虽然可以实现深拷贝,但是会存在缺陷,不能处理正则和函数,因为通过JSON.stringify和JSON.parse处理后的正则会变成空对象,函数会变成null。
-
各个类库的实现,如jquery的$.extend,lodash的_.cloneDeep
-
手写递归
遍历对象、数组,直到里面的元素都是基本类型再去赋值,(有种特使情况:对象存在循环引用的情况,即对象的属性直接引用了自身的情况,解决的方法 可以开辟一个存储空间来存储当前对象和拷贝对象的关系,当需要拷贝当前对象时,根据存储空间判断有没有拷贝过这个对象,如果没有的话就继续拷贝,如果有的话就返回存储空间里面的值)
实现步骤:
-
判断参数是否为空 => 返回 {}
-
判断参数是否为Date或者Reg类型=> 返回对应类型 (new Date/ new RegExp)
-
判断参数是否为Objec类型 => 不是的情况直接返回参数
-
创建新的对象(继承参数)
-
遍历新对象,调用自身函数
-
返回新对象
-