深浅拷贝你知道多少?
原文链接:https://juejin.cn/post/6844903608010407944
一、基本数据类型 & 引用数据类型
ECMAScript数据类型可分为两种:
- 基本类型:Number、String、Boolean、null、undefined、Symbol;
- 引用类型:Object、Array、function、Date、RegExp。
不同类型的数据存储方式:
- 基本类型:基本类型值在内存中占据固定大小,保存在栈中
- 引用类型:引用类型的值是对象,保存在堆内存中,而栈内存存储的是对象的变量标识符以及对象在堆内存中的存储地址
不同类型的复制方式:
- 基本数据类型:从一个变量向另一个新变量复制基本类型的值,会创角这个值的一个副本,并将该副本赋值给新变量,修改源数据,并不会产生连锁反应。
let foo = 1;
let bar = foo;
console.log(foo === bar); // -> true
// 修改foo变量的值并不会影响bar变量的值
let foo = 233;
console.log(foo); // -> 233
console.log(bar); // -> 1
- 引用数据类型:从一个变量向另一个新变量复制引用类型的值,其实复制的是指针(栈中存储对象在堆中的物理地址),最终两个变量都指向同一个对象,修改其中一个,另一个也会变更。
let foo = {
name: 'leeper',
age: 20
}
let bar = foo;
console.log(foo === bar); // -> true
// 改变foo变量的值会影响bar变量的值
foo.age = 19;
console.log(foo); // -> {name: 'leeper', age: 19}
console.log(bar); // -> {name: 'leeper', age: 19}
二、深拷贝 & 浅拷贝
- 浅拷贝:仅仅只是复制了对象存储在栈中的引用地址,彼此之间的操作会相互影响
- 深拷贝:在堆中重新分配内存,不同的堆中地址,相同的值,互不影响
总的来说,两者主要的区别是:复制的是引用还是实例
三、深浅拷贝的实现
当采用 =
号进行赋值的时候,
3.1 浅拷贝
3.1.1 Array.prototype.slice()
和 Array.prototype.concat()
let a = [[1,2],2,3,4]
let b = a.slice() // let b = a.concat()
a[0][0] = 10
a[1] = 20
console.log('b', b) // [[10, 2], 2, 3, 4]
// 对于第一层元素是深拷贝,第二层则是浅拷贝
3.1.2 Object.assign(target, ...sources)
注意目标对象自身也会改变,继承属性和不可枚举属性是不能拷贝的
let obj = {
name: 'shengjingyin',
cc: {
name:"juliya"
},
sayHi: function () {
console.log('hello:', this.name)
}
}
let newObj = Object.assign({},obj)
obj.name = 'juli'
obj.cc.name = 'juli'
console.log('newObj', newObj) // {name: "shengjingyin", cc: {name: "juli"}, sayHi: ƒ}
针对深拷贝,需要使用其他办法,因为 Object.assign()
拷贝的是(可枚举)属性值。
假如源值是一个对象的引用(譬如:上面事例 的 cc 属性),它仅仅会复制其引用值
3.2 深拷贝
3.2.1 JSON.parse(JSON.stringify())
let a = [[1,2],2,3,4]
let b = JSON.parse(JSON.stringify(a))
a[0][0] = 5
a[1] = 20
console.log('b', b) // [[1, 2], 2, 3, 4]
// 对于第一层、第二层元素都是深拷贝
let obj = {
name: 'shengjingyin',
sayHi: function () {
console.log('hello:', this.name)
}
}
let newObj = JSON.parse(JSON.stringify(obj))
console.log('newObj', newObj)
// 无法复制sayHi 函数
缺点:无法序列化对象中的函数,新复制出来的对象不包含原对象的函数
3.2.2 递归
function deepClone(target) {
if (target == null || typeof target !== 'object') {
return target
}
let clone = {}
if (target instanceof Array) {
clone = []
}
for (let key in target) {
if (target.hasOwnProperty(key)) {
clone[key] = deepClone(target[key])
}
}
return clone
}