所谓拷贝就是赋值。把一个变量赋给另外一个变量,就是把变量的内容进行拷贝;把一个对象的值赋给另一个对象就是把一个对象拷贝一份。这两种拷贝方式常用于拷贝对象
为什么会有深拷贝和浅拷贝之分,就是因为我们通常会进行拷贝的值类型分为两种,一种是基本数据类型,一种是复杂数据类型。
拷贝基本数据(浅拷贝):
基本数据类型也称为值类型。变量对应的内存空间放的是一个值,比如 let a = 100,a这个变量对应的内存地址存放的值就是100,一个确切的值。 所以当我们需要拷贝基本数据类型时,可以直接将变量对应的内存地址的值复制一份,开辟一个新的空间存放,不会和以前的内存空间产生冲突,这种拷贝方式就是浅拷贝。
常用es6种的扩展运算符和Object.assign()进行浅拷贝
let obj = {
name:'zs',
age:18,
}
// 使用扩展运算符浅拷贝
let newObj = {...obj}
//使用Object.assign()
let newObj1 = Object.assign({},obj)
拷贝复杂数据类型(深拷贝):
-
- 复杂数据类型就是地址类型,也叫引用类型。变量对应的内存空间存放的是一个地址, 地址对应的内存空间存放的才是真正的数据。就好比我们填写简历是要求写居住地址,我们并不会把我们整个家搬来放在简历上,只会填写对应的地址,根据地址就能找到真正居住的地方。
-
- 因为复杂数据类型本身变量对应的内存空间存放的就是地址,如果使用浅拷贝的方式,拷贝过去的依然是地址,这是就会出现两个变量公用一个地址的数据,只要有一个变量修改了这个地址里的值,另一个变量也会收到影响,这种现象并不是我们想要的,所以此时我们对于这种复杂数据类型的拷贝就需要用到
深拷贝。
- 因为复杂数据类型本身变量对应的内存空间存放的就是地址,如果使用浅拷贝的方式,拷贝过去的依然是地址,这是就会出现两个变量公用一个地址的数据,只要有一个变量修改了这个地址里的值,另一个变量也会收到影响,这种现象并不是我们想要的,所以此时我们对于这种复杂数据类型的拷贝就需要用到
实现深拷贝的几种方法:
- 使用JSON对象的方式
let Obj = {
name: 'zs',
age: 20,
hobby: ['打游戏', '看动漫', '哈啤酒'],
father: {
name: 'ls',
age: 45,
hobby: ['搓麻将', '斗地主'],
},
a: /^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$/,
b: new Date(),
c: null,
d: Symbol(1),
sing: function () {
console.log('唱歌')
},
}
window.localStorage.setItem('obj', JSON.stringify(Obj))
let newObj = JSON.parse(window.localStorage.getItem('obj'))
console.log(newObj)
{
a: {}
age: 20
b: "2022-07-25T07:13:24.336Z"
c: null
father: {name: "ls", age: 45, hobby: ["搓麻将", "斗地主"]}
hobby: ["打游戏", "看动漫", "哈啤酒"]
name: "zs"
}
此时我们不难发现,使用JSON进行深拷贝有风险
- 造成数据丢失和数据异常
- function、undefined 直接丢失
- NaN、Infinity 和-Infinity 变成 null
RegExp、Error对象只得到空对象;
2.利用递归进行深拷贝
let Obj = {
name: 'zs',
age: 20,
hobby: ['打游戏', '看动漫', '哈啤酒'],
father: {
name: 'ls',
age: 45,
hobby: ['搓麻将', '斗地主'],
},
a: /^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$/,
b: new Date(),
c: null,
d: Symbol(1),
sing: function () {
console.log('唱歌')
},
}
function deepCopy(obj) {
let newObj = Array.isArray(obj) ? [] : {}
for (let key in obj) {
if (obj[key] instanceof Array) {
newObj[key] = []
newObj[key] = deepCopy(obj[key])
} else if (obj[key] instanceof Object) {
newObj[key] = {}
newObj[key] = deepCopy(obj[key])
} else {
newObj[key] = obj[key]
}
}
return newObj
}
consoloe.log(Obj)
{a: {}
age: 20
b: {}
c: null
d: Symbol(1)
father: {name: 'ls', age: 45, hobby: Array(2)}
hobby: (3) ['打游戏', '看动漫', '哈啤酒']
name: "zs"
sing: {}}
我们不难看出递归也存在上述问题,也可能出现数据异常和数据丢失,并且当要拷贝的对象存在循环引用,也就是对象的某个属性值是这个对象,就会出现死循环,堆栈溢出的问题。
解决思路:
将每次拷贝的数据进行存储,每次在拷贝之前,先看该数据是否拷贝过,如果拷贝过,直接返回,不在拷贝,如果没有拷贝,对该数据进行拷贝并记录该数据以拷贝
可以使用数组,或者new Map()developer.mozilla.org/zh-CN/docs/…
在实际开发中我们常用使用 lodash(www.lodashjs.com/) 实现深拷贝