说道深拷贝, 浅拷贝, 我们得从js的数据类型开始讲起.
一. js的数据类型分为基础数据类型 和 引用数据类型
1. 基础数据类型: string, Namber, Boolean, undifind, null
2. 引用数据类型: Array, Object, Function... 等
两种数据类型的区别在于, 储存的方式不一样, 基础数据类型是直接将数据存放在栈中;
而引用数据类型是将地址存放在栈中,而真实数据存放在堆中, 栈中的地址指向堆中的数据.
变量在做赋值运算时, 都是将栈中的数据给等号右边, 所以两种数据在做赋值运算时会出现下面情况:
// 基础数据
let a = 1
let b = a
b = 2
console.log(a, b) // 1, 2 不会出现什么特殊情况
// 引用数据类型
let obj1 = {a: 1, b: 2}
let obj2 = obj1
obj2.a = 3
console.log(obj1) // {a: 3, b: 2}
是不是很奇怪, 我们改变obj2, obj1为什么也改变了; 是因为obj1是一个对象, 真实数据是存放在堆中, 而 let obj2 = obj1 赋值时是将obj1栈中的地址给了obj2 他们栈中的地址是一样的, 所以会指向同一个堆中的数据, 这就是为什么改变obj2中的属性, obj1 也会一起变化
那么问题就来了, 如果要将引用数据类型的变量赋给另一个变量, 并且让两个变量完全独立, 相互不影响, 我们可以用如下方法
//1. 最简单方法
let obj1 = {a: 1, b: 2}
let obj2 = {...obj1}
obj2.a = 3
console.log(obj1) // {a: 1, b: 2}
console.log(obj2) // {a: 3, b: 2}
// 两个变量就独立了
上述方法: 可以实现将两个变量独立起来, 但是也只能拷贝一层数据,也就是浅拷贝 不过日常开发使用的也最多;
现在如果数据有多层, 也就是对象里面嵌套着对象
let obj1 = {a: {aa: 1, ab: 2}, b: {ba: 2, bb: 4}}
let obj2 = {...obj1}
obj2.a.aa = 5
obj1.a.aa = 5 // 还是5
这里如果想要将两个对象完全独立, 就要涉及到深拷贝了
深拷贝方案有两种
1 JSON
let obj1 = {a: {aa: 1, ab: 2}, b: {ba: 2, bb: 4}}
let obj2 = JSON.parse(JSON.stringify(obj1))
这样两个对象就完全独立了,但是此方法还是存在一个问题 就是如果 obj1 中如果有方法 就不能这么用了, 函数拷贝不过来; obj2最后只能是一个空对象
let obj1 = {a: function(){console.log('aaa')}}
let obj2 = JSON.parse(JSON.stringify(obj1))
要解决上面的问题就要用到最令人头疼的递归了,
var obj = {
name: '小王',
hobby: ['reading', 'running']
}
// 对于当前要拷贝的内容,如果还是一个对象,则要递归调用,而不是直接赋值
function deepCopy(obj) {
// 定义一个空的对象或者是数组
// 如果当前要复制的是数组就用一个空数组,或者用一个空对象。
// var tempObj = Array.isArray(obj) ? [] : {}
var tempObj = {}
if (Array.isArray(obj)) {
tempObj = []
}
// var tempObj = {}
// .... 操作
for (var key in obj) {
var t = obj[key]
// 当要拷贝的内容还是一个对象或者是数组
if (Array.isArray(t) || typeof t === "object") {
tempObj[key] = deepCopy(obj[key])
}
else // 当要拷贝的内容只是一个基本类型的数据
{
tempObj[key] = t
}
}
return tempObj
}
var newObj = deepCopy(obj);
// 验证:
// 给 obj1.hobby.添一个信息,不会影响obj.hobby.
obj1.hobby === obj.hobby; // false
// 简化写法
function isObj(obj) {
return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function deepCopy(obj) {
var tempObj = Array.isArray(obj) ? [] : {}
for (var key in obj) {
tempObj[key] = isObj(obj[key]) ? deepCopy(obj[key]) : obj[key]
}
return tempObj
}