前言
在开发中经常会遇到,将对象A赋值给对象B后修改对象B但对象A的值也随之修改变了,定义对象时定义的变量是引用类型,而对象之间的赋值其实只是将引用的内存地址复制了而已,本质上修改的却是同一个对象。
因为浅拷贝的特性,和开发中开发者的思维逻辑并不同步,也随之而来的带来了很多BUG,而浅拷贝自然而然的被诟病,深拷贝有几种实现方式这种文章的热度一直是居高不下。那么浅拷贝就这么不堪吗?我们今天来看看浅拷贝有哪些妙用!
赋值和深/浅拷贝的区别
在开始之前,还需了解深/浅拷贝的区别,以更好的开始。
- 浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。
- 深拷贝:从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响。
如:
// 浅拷贝
const obj1 = {
name: 'mokevip',
age: 21
}
const obj2 = obj1
obj2.name = 'newname'
console.log(obj1.name) // newname
// 深拷贝(JSON转换)
// 直接转换简单数据类型,不能转换方法和正则等复杂类型,这里只是简单介绍,需要完整拷贝可以使用递归方法
const obj1 = {
name: 'mokevip',
age: 21
}
const obj2 = JSON.parse(JSON.stringify(obj1))
obj2.name = 'newname'
console.log(obj1.name) // mokevip
复制代码
关于拷贝的深度
定义一个对象时,变量本身为引用类型,当对象嵌套时,上一层对象保存的也是下层对象在堆内存中的内存地址。
对象本身只能保存基础类型和下层对象或函数的引用地址,因此,拷贝有深度,这也是为何深拷贝时需要递归到最后一层的原因。
灵活使用浅拷贝
已知存有员工列表对象workers和员工对象worker,由于我们需要对员工对象进行多选和编辑,有哪几种方法呢?
例1 对象内新增selected标识选中
worker = {
name: '张三',
idcard: '320924',
selected: false,
price: 0,
star: 0
}
workers = [worker, worker, worker]
function select(index){
workers[index].selected = true
}
function edit(){
workers = workers.map(item=>{
if(item.selected){
// 该方法需要通过遍历找到所有标识选中的数据
}
})
}
<div class="selected" v-if="item.selected"></div>
复制代码
例2 额外维护选中列表的selects数组(适用于分页)
例1中的方法,在需要做前端分页时就容易顾此失彼,因为分页需要两个数组去实现,一个为原数组一个为显示的数组,此时两个数组需要通过一个唯一组件联系起来。
worker = {
name: '张三',
idcard: '320924',
selected: false,
price: 0,
star: 0
}
workers = [worker, worker, worker]
showWorkers = [worker, worker, worker]
selects = []
function select(index){
selects.push(showWorkers[index].idcard) // 这里考虑到删除,不能直接使用index
}
function edit(){
workers.map(item=>{ if(item.idcard in selects){
// 同样需要通过遍历找到所有标识选中的数据,但是由于只存了idcard作为主键所以在
}
})
}
<div class="selected" v-if="item.idcard in selects"></div>
复制代码
例3 通过浅拷贝实现(例2的升级)
涉及分页时我们可以直接用worker对象本身作为主键,由于浅拷贝,涉及到worker对象的修改,无论是修改哪一个数组都是对workers、showWorkers、selects中的这一worker的同时修改,因为修改的是同一内存地址上的内容。
worker = {
name: '张三',
idcard: '320924',
selected: false,
price: 0,
star: 0
}
workers = [worker, worker, worker]
showWorkers = [worker]
selects = []
function select(index){
selects.push(showWorkers[index]) // 直接将worker对象本身作为选中标定
}
function edit(){
for(let i in selects){
selects[i].price = 3 // 此时对selects中worker对象的操作由于浅拷贝,其实是对workers、showWorkers、selects中的这一worker的同时修改(因为修改的是同一内存地址上的内容)
}
}
<div class="selected" v-if="item in selects"></div>
复制代码
小结
该方法只是浅拷贝特性利用的一个使用场景,当然其他的方法和场景也有很多。
很多时候,我们在维护对象时,不需要一定使用深拷贝、一定使用浅拷贝,我们可以尝试转换思路,这些对象使用不同深度的拷贝方法会不会更好?就像黑与白之间存在着灰,也没有绝对的深拷贝和浅拷贝。
总之,当我们需要在多个数组中维护统一类型的对象时,不妨使用浅拷贝试试。