深拷贝和浅拷贝问题的来由
这个问题归根到底还得从js的数据存出方式说起,js大体上分为两种数据类型,简单数据类型和复杂数据类型。简单数据类型因为其大小固定,故存储在栈内存中,可以直接访问;而复杂数据类型大小可变,故存储在堆内存中,然后再在栈内存当中存储一个指向堆内存的那块存储区域的地址,当我们要访问这个复杂数据类型的时候,我们就根据栈内存的地址找到堆内存中的它访问,即通过栈内存中的指针间接访问。
总结一下上面这段话:
1、简单数据类型:大小固定,存储在栈中,可以直接访问,包括number、string、Boolean 、undefined、null;
2、复杂数据类型:大小不定,存储在堆内存中,栈内存存储一个指向堆内存的地址,需间接访问,包括Object 、Array 、Date 、function;
存储方式的不同致使产生深、浅拷贝
浅拷贝:
function easyCopy(obj) {
var cpObj = {}
for (var key in obj) {
cpObj[key] = obj[key]
}
return cpObj
}
var obj_1 = {
name: '叶问',
sex: 'man',
hobby: ['咏春', '怕老婆'], // 复杂数据类型
}
var obj_2 = easyCopy(obj_1)
console.log(obj_2)
控制台的结果:
此时obj_2成功的从obj_1拷贝过来了
接下来,我要开始操作了,我将修改obj_2的hobby属性
代码:
function easyCopy(obj) {
var cpObj = {}
for (var key in obj) {
cpObj[key] = obj[key]
}
return cpObj
}
var obj_1 = {
name: '叶问',
sex: 'man',
hobby: ['咏春', '怕老婆'], // 复杂数据类型
}
var obj_2 = easyCopy(obj_1)
// console.log(obj_2)
obj_2['hobby'].push('打架') // 此处修改obj_2
console.log(obj_1) //打印obj_1
结果:
此时,有人可能会有疑问了,我明明修改的是obj_2,关obj_1什么事呢,但是结果就是obj_1的hobby数组多了一个元素: “打架”。真是见鬼了…
下面,将由我为大家解答
首先,我们分析一下obj_1对象的组成,有name、sex、hobby。
name、sex:字符串,基本数据类型,存储在栈内存当中;
hobby:数组,复杂数据类型,存储在堆内存当中,栈内存当中存储一个指向堆内存的指针;
其实,说白了,我们复制的obj_2,它的name、sex复制的是栈内存当中的值,因为栈内存当中的值可以直接访问,所以复制的就是name:“叶问”,sex:“man”;而hobby是复杂数据类型,栈内存当中仅仅存储一个地址,所以,复制的obj_2的bobby属性其实是一个地址,指向的是obj_1的hobby在堆内存当中的那块存储区域,所以,你改obj_2的hobby属性值,其实就是在改obj_1的hobby指向的堆内存存储区域里面的内容,换句话说,obj_1和obj_2的hobby属性都是地址,指向同一个堆内存区域。所以,你改obj_2的hobby,其实就是在改obj_1的hobby。结果就是,obj_1的hobby数组多了一个元素“打架”。
深拷贝
当然,浅拷贝在很多种情况下跟我们的意愿是相违背的,我们不想要类似这样的结果,obj_1和obj_2关联,我们希望我改obj_2的属性不影响obj_1,那我们就需要用到深拷贝了。
深拷贝主要思想就是利用递归(自己调用自己)实现。
代码:
var obj_1 = {
name: '叶问',
sex: 'man',
hobby: ['咏春', '怕老婆'], // 复杂数据类型
}
function deepCopy(q, m) {
var m = m || {}
for (var i in q) {
if (typeof q[i] === 'object') {
m[i] = (q[i].constructor === Array) ? [] : {} //此处用对象的构造函数判断其类型
deepCopy(q[i], m[i]) //此处在自己调用自己
} else {
m[i] = q[i]
}
}
return m
}
var obj_3 = deepCopy(obj_1)
obj_3['hobby'].push('打架')
console.log(obj_1)
console.log(obj_3)
结果:
此时,我通过深拷贝deepCopy函数的到obj_3,我向obj_3的hobby数组添加了"打架"这个元素,打印obj_1的结果表明:obj_1的hobby数组并没有添加"打架"这个元素,最终得到结论:修改obj_3的bobby属性,不会影响obj_1的hobby属性。