为什么对象中的值总是拷贝不下来
前言
想必大家在编程中总会遇到这样一个问题,对象复制完后里面的值总是不稳定的,当我们试图拷贝一个对象的值时,经常会发现原以为的深拷贝实际上变成了浅拷贝,那么这到底是为什么呢?
对象的本质
首先,我们得弄清楚对象的本质。在JavaScript中,对象是一种引用类型。当你声明一个对象时,实际上是创建了一个指向内存中某处数据的引用,通俗来说就是创建了一个地址,然后js把你在对象里面声明的值放在了这个地址里面。例如:
let obj = { name: "apple" };
let objclone = obj;
这里的obj
并不是直接保存了对象本身,而是保存了对象在内存中的地址。所以当你尝试将obj
赋值给objclone
时,实际上是将这个引用传递给了新的变量:
objclone.name = "bigapple";
console.log(obj.name); // 输出:"bigapple"
可见,当我们更改objclone
中name的值时,obj
的name也随之改变,因为它俩其实就是一个东西。对象的赋值操作并没有真正创建一个新的对象,而是共享了相同的引用,这也就是我们所说的浅拷贝。
还有哪些操作属于浅拷贝
其实我们平常用到的绝大多数拷贝都属于浅拷贝:
let obj = {
name:'apple'
}
obj1=Object.create(obj) // 通过原型创建一个新对象
obj2=Object.assign({},obj) //创建一个新对象并复制对象的顶层属性
let arr = [1,2,obj] //创建一个包含该对象的数组
let arr1 = [...arr] //解构再重组数组
let arr2 = arr.toReversed().reverse() //翻转再翻转
let arr3 = [].concat(arr) //与空数组合并
let arr4 = arr.slice(0) //从下标为零开始切割
obj.name = 'oneapple' //修改初始对象中的name
console.log(obj1.name); // 输出:'oneapple'
console.log(obj2.name); // 输出:'apple'
console.log(arr1); // 输出:[ 1, 2, { name: 'oneapple' } ]
console.log(arr2); // 输出:[ 1, 2, { name: 'oneapple' } ]
console.log(arr3); // 输出:[ 1, 2, { name: 'oneapple' } ]
console.log(arr4); // 输出:[ 1, 2, { name: 'oneapple' } ]
那么肯定有人会问了,Object.assign({},obj)
拷贝的值不是没改变吗?那么我们就探讨一下它为什么属于浅拷贝。它会创造一个新对象,但它只会复制对象的顶层属性,而不会复制嵌套的属性或对象。
举个例子来说明:
let obj = {
name: "apple",
age: 18,
address: {
city: "shanghai",
country: "China"
}
};
let objclone = Object.assign({}, obj);
obj.address.city = "shenzhen"
console.log(objclone);
// 输出:{name: 'apple',age: 18,address: { city: 'shenzhen', country: 'China' }}
apple还是变成深圳的了,所以说,这也只能算是浅拷贝,obj.address
与 objclone.address
还是指向内存中的同一位置。
浅拷贝的局限性:
那么了解了这些后,浅拷贝的局限性显而易见,即使你认为自己已经拷贝了对象,实际上仍然共享着内部对象的引用,导致任何在原对象的修改都会反映在该对象上。
深拷贝的实现
那么深拷贝该如何实现呢?怎样才能使新创建的对象与原来的对象完全断绝“血缘关系”。
使用JSON序列化与反序列化:
利用JSON.stringify()
将对象转换为字符串,再使用JSON.parse()
将其解析回对象:
let obj = {
b:{n:2},
g:function(){},
h:Symbol(1)
}
let newObj = JSON.parse(JSON.stringify(obj))
obj.b.n = 3
console.log(newObj); //输出:{ b: { n: 2 } }
可见,这种方法的缺陷就是不能正确处理函数和Symbol类型的属性。
递归函数实现:
自定义一个递归函数,检查每个属性是否是对象或数组,如果是,就递归地进行深拷贝:
let obj = {
a:1,
b:{n:2}
}
function deepCopy(obj){
let newObj = {}
for(let key in obj){
if(obj.hasOwnProperty(key)){
// obj[key] 是不是对象
if(obj[key] instanceof Object){
newObj[key] = deepCopy(obj[key])
}else{
newObj[key] = obj[key]
}
}
}
return newObj
}
let newObj = deepCopy(obj)
obj.b.n=3
console.log(newObj); //输出:{ a: 1, b: { n: 2 } }
这便完成了一个实实在在的深拷贝!!!
结语
那么,学会深拷贝,妈妈再也不用担心我们拷贝的对象会因为原对象的改变而改变了。摆脱各种编程陷阱,从我做起,咱自个儿创建一个实打实的对象。
newobj = obj
转载自:https://juejin.cn/post/7387801635328032777