对于js中的对象的深拷贝在项目的开发中比较常用到,本篇文章举例说明常用的js对象的深拷贝方式。以供开发中使用。废话不多说,先上常用深拷贝方式结论,结论后面的是对应分析
常用深拷贝方式
- JSON.parse(JSON.stringify())
- Object.assign
- ...拓展运算符
- lodash函数库
关于js中变量(数据类型)的复制
我们知道,js语言中常用的有基本数据类型和引用数据类型
基本数据类型
基本数据类型分类
- string
- number
- boolean
- null
- undefined
基本数据类型复制
由于基本数据类型的值存在栈里面,所以直接使用一个等于号=
就可以复制,但是引用数据类型栈里面保存的只是一个指针地址,指针指向的是堆里面的数据。所以关于引用数据类型的复制,会稍微麻烦一点。
引用数据类型
引用数据类型分类
- 对象
- 数组
- 函数
引用数据类型复制
我们知道,数据的复制(拷贝)其实本质上就是单独找一个内存块来存放对应的特定信息,而且这部分的信息是与原来的独立分开的。
对于引用数据类型的复制,如果直接使用等于号来赋值拷贝的话,只是把引用数据类型的指针给了该变量,所以就要说说引用数据类型的拷贝。函数一般不需要做拷贝,因为函数在js中是一等公民,哪里需要使用直接调用一下函数即可,所以谈到深拷贝或浅拷贝这个词的时候,一般指的是对象或者数据的拷贝,对象的复制略微多用一点
对象的深拷贝
浅拷贝,直接等于号=
赋值,这里就不赘述了
场景假设
假设我们的项目页面上有一个表格,表格中每一行都是对应的数据,在表格的最右侧有一个编辑按钮,点击编辑按钮,出现一个弹出框,弹出框中有表单,表单中出现表格中对应行的数据,以供我们编辑修改保存。具体逻辑也很简单,即点击某一行编辑按钮,拿到对应行的数据,将对应行的数据赋到表单中。下面两张图分别呈现对应不使用深拷贝和使用深拷贝的效果区别
不使用深拷贝效果图
不使用深拷贝,直接将rowData赋值到表单中去,我们会发现,当我们修改表单中的数据的时候,表格中对应行的数据居然也会被修改,因为我们赋值过去的只是对象的指针引用地址,所以出现这样的效果。这显然不是我们想要的,所以这种方式,一般不可行。
使用深拷贝效果图
我们发现使用深拷贝,修改表单中的数据,倒是没有改变原表格中对应行的数据,这才是我们想要的。
对应代码
<template>
<div id="app">
<!-- 表格部分 -->
<el-table :data="tableData" border style="width: 100%">
<el-table-column prop="name" label="姓名" width="180"> </el-table-column>
<el-table-column prop="age" label="年龄" width="180"> </el-table-column>
<el-table-column prop="home" label="家乡"> </el-table-column>
<el-table-column fixed="right" label="操作" width="100">
<template slot-scope="scope">
<el-button type="primary" plain size="small" @click="editRow(scope.row)"
>编辑</el-button
>
</template>
</el-table-column>
</el-table>
<!-- 弹框中 表单部分 -->
<el-dialog
title="编辑表格"
append-to-body
:close-on-click-modal="false"
:visible.sync="dialogVisible"
width="30%"
>
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="姓名">
<el-input v-model.trim="form.name"></el-input>
</el-form-item>
<el-form-item label="年龄">
<el-input v-model.trim="form.age"></el-input>
</el-form-item>
<el-form-item label="家乡">
<el-input v-model.trim="form.home"></el-input>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
// 引入lodash函数工具库
import _ from 'lodash'
export default {
components: {},
data() {
return {
// 表格的数据
tableData: [
{
name: "孙悟空",
age: "500",
home: "花果山水帘洞",
},
{
name: "猪八戒",
age: "88",
home: "高老庄",
},
],
dialogVisible: false,
// 表单的数据
form: {
name: "",
age: "",
home: "",
},
};
},
methods: {
editRow(rowData) {
console.log("rowData", this.form);
this.dialogVisible = true;
// // 不用深拷贝,直接赋值
// this.form = rowData
// // 深拷贝方式一 之 JSON方法
// this.form = JSON.parse(JSON.stringify(rowData));
// // 深拷贝方式二 之 Object.assign方法
// this.form = Object.assign({},rowData)
// // 深拷贝方式三 之 拓展运算符
// let { ...newObj } = rowData
// this.form = newObj
// // 深拷贝方式四 之 lodash函数库
this.form = _.cloneDeep(rowData) // 调用lodash的cloneDeep方法也可以做深拷贝
},
},
};
</script>
<style lang="less" scoped>
#app {
width: 100%;
height: 100vh;
box-sizing: border-box;
padding: 50px;
}
/deep/ .el-dialog {
margin-top: 30vh !important;
}
</style>
补充数组的深拷贝
对于数组的拷贝复制而言,如果直接使用等于号=
来进行拷贝复制,那只是浅拷贝,浅拷贝拷贝的是地址,所以源数组改变,拷贝的数组也会跟着改变
常见方式
- slice
- concat
- ...拓展
- JSON.parse(JSON.stringify())
let arr = ['孙悟空','猪八戒','沙和尚','唐僧']
// 方式一slice
let newArr = arr.slice(0)
// 方式二concat
let newArr = arr.concat()
// 方式三...拓展运算符
let [ ...newArr ] = arr
// 方式四
let newArr = JSON.parse(JSON.stringify(arr))
当然也可以使用lodash的函数工具库
手写一个深克隆
let arr = [
{
name: '孙悟空',
age: 500,
skill: ['筋斗云', '火眼金睛']
},
{
name: '猪八戒',
age: 88,
skill: ['九齿钉耙', '36变']
},
]
function deepClone(params) {
// 如果数组类型数据
if (Array.isArray(params)) {
let newnew = []
for (let i = 0; i < params.length; i++) {
newnew[i] = deepClone(params[i]) // 递归调用克隆
}
return newnew // 克隆完以后,再返回出结果
}
// 如果是对象类型数据
if (Object.prototype.toString.call(params) === '[object Object]') {
let newnew = {}
for (const key in params) {
newnew[key] = deepClone(params[key]) // 递归调用克隆
}
return newnew // 克隆完以后,再返回出结果
}
// 如果是普通数据类型
return params
}
console.log( deepClone(arr) );
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。