JavaScript - 深浅拷贝

前言

JS 中的数据类型可分为两种:

  • 基本类型:undefined , null , Boolean , String , Number , Symbol
  • 引用类型:Object , Array , Date , Function , RegExp

不同类型的存储方式:

  • 基本类型:基本类型值在内存中占据固定大小,保存在 栈内存
  • 引用类型:引用类型的值是对象,保存在 堆内存 中,而 栈内存 中存储的是对象的 变量标识符 以及对象在 堆内存 中的存储地址

不同类型的复制方式:

  • 基本类型:从一个变量向另外一个新变量复制基本类型的值,会创建这个值的一个副本,并将该副本复制给新变量
  • 引用类型:从一个变量向另一个新变量复制引用类型的值,其实复制的是储存地址,最终两个变量最终都指向同一个对象

深拷贝和浅拷贝的区别

  • 浅拷贝:仅仅是复制了引用,彼此之间的操作会互相影响
  • 深拷贝:在堆中重新分配内存,不同的地址,相同的值,互不影响

浅拷贝

1、可通过赋值实现

javascript
12345678910111213141516171819202122232425262728293031
function shallowClone(object) {
// 只拷贝对象
if (!object || typeof object !== 'object') return
// 根据 object 的类型判断是新建一个数组还是对象
let newObject = Array.isArray(object) ? [] : {}
// 遍历 object,并且判断是 object 的属性才拷贝
for (let key in object) {
if (object.hasOwnProperty(key)) {
newObject[key] = object[key]
}
}
return newObject
}
let obj = {
a: 'hello',
b: {
c: 'js',
},
}
let newObj = shallowClone(obj)
newObj.a = 'HELLO'
newObj.b.c = 'JS'
console.log(obj.a) // => hello
console.log(obj.b.c) // => JS

2、可通过 Object.assign 实现

javascript
1234567891011121314
let obj = {
a: 'hello',
b: {
c: 'js',
},
}
let newObj = Object.assign({}, obj)
newObj.a = 'HELLO'
newObj.b.c = 'JS'
console.log(obj.a) // => hello
console.log(obj.b.c) // => JS

3、可通过 扩展运算符(...) 实现

javascript
1234567891011121314
let obj = {
a: 'hello',
b: {
c: 'js',
},
}
let newObj = { ...obj }
newObj.a = 'HELLO'
newObj.b.c = 'JS'
console.log(obj.a) // => hello
console.log(obj.b.c) // => JS

深拷贝

1、可通过 JSON.parse(JSON.stringify(object)) 实现

javascript
1234567891011121314
let obj = {
a: 'hello',
b: {
c: 'js',
},
}
let newObj = JSON.parse(JSON.stringify(obj))
newObj.a = 'HELLO'
newObj.b.c = 'JS'
console.log(obj.a) // => hello
console.log(obj.b.c) // => js

但是该方法也是有局限性的:

会忽略 undefined 会忽略 symbol 不能序列化函数 不能解决循环引用的对象

2、可通过递归实现

javascript
1234567891011121314151617181920212223242526272829
function deepClone(obj) {
if (obj === null) return obj
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
if (typeof obj !== 'object') return obj
let cloneObj = new obj.constructor()
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key])
}
}
return cloneObj
}
let obj = {
a: 'hello',
b: {
c: 'js',
},
}
let newObj = deepClone(obj)
newObj.a = 'HELLO'
newObj.b.c = 'JS'
console.log(obj.a) // => hello
console.log(obj.b.c) // => js