一定要深拷贝?浅谈JS中浅拷贝的妙用。

/ 0评 / 0

前言

在开发中经常会遇到,将对象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>
复制代码

小结

该方法只是浅拷贝特性利用的一个使用场景,当然其他的方法和场景也有很多。

很多时候,我们在维护对象时,不需要一定使用深拷贝、一定使用浅拷贝,我们可以尝试转换思路,这些对象使用不同深度的拷贝方法会不会更好?就像黑与白之间存在着灰,也没有绝对的深拷贝和浅拷贝。

总之,当我们需要在多个数组中维护统一类型的对象时,不妨使用浅拷贝试试

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注