前言
在JavaScript的编程世界中,对于数据的复制操作是一项简单且基本的操作,其中深浅拷贝更是其重要应用,本文将从深浅拷贝的概念,实现方法,和一些场景的应用做一个简要分析,助你在编程时能根据具体需求做出正确选择。
前置小知识
我们都应该了解JavaScript中有两大数据类型,第一类是基本数据类型,包括了Number,String,Boolean等
第二大类就是我们的引用数据类型,包括我们熟悉的Object``Array``Function等
从js的存储机制来看,基本数据类型是存储在栈中的,而引用数据类型的存储情况是:数据的值是存储在堆中,而栈中存储的是数据的引用,也就是地址
let obj1={a:1,b:2}
let obj2=obj1 //这里的obj1其实是引用地址,相当于引用复制,一般来说是不算浅拷贝的,因为没有生成新的对象,相当于obj1和obj2指向同一片内存空间
基本概念:揭开深浅拷贝的神秘面纱
浅拷贝:共享内存的“镜中影像”
当创建一个浅拷贝对象时,对于浅拷贝内部,如果是基本数据类型,会直接复制其值,两者独立。而对于引用数据类型会复制其存储在栈中的地址,这就相当于复制前后的对象共享同一块内存空间,那么这样会发生什么呢?我们不妨举个例子
//假如我们有一个原始对象original
let original = {a:1,b:{c:2}}
//我们使用Object.assign()方法浅拷贝一个对象为shallowCopy
const shallowCopy = Object.assign({}, original)
// 修改拷贝对象中嵌套的属性
shallowCopy.b.c = 200 // 由于是浅拷贝,b属性的引用指向同一个对象
// 打印原对象和拷贝对象
console.log("原对象 original:", original)
console.log("拷贝对象 shallowCopy:", shallowCopy)
//原对象 original: { a: 1, b: { c: 200 } }
//拷贝对象 shallowCopy: { a: 1, b: { c: 200 } }
当我们修改 shallowCopy.b.c 的值时,原对象 original.b.c 的值也会随之改变。这就好比两个人共用一个钱包,其中一个人花了钱,另一个人钱包里的钱也会相应减少。
深拷贝:完全独立的“孪生兄弟
深拷贝会递归地复制对象的所有属性,无论是基本数据类型还是引用数据类型,都会在内存中开辟新的空间进行存储。因此,修改新对象不会对原对象产生任何影响,反之亦然。
还是以上面的对象为例
// 使用JSON.parse(JSON.stringify())进行深拷贝
const deepCopy = JSON.parse(JSON.stringify(original));
// 修改拷贝对象中嵌套的属性
deepCopy.b.c = 200 // 由于是深拷贝,b属性指向全新的对象
// 打印原对象和拷贝对象
console.log("原对象 original:", original)
console.log("深拷贝对象 deepCopy:", deepCopy)
//原对象 original: { a: 1, b: { c: 2 } }
//深拷贝对象 deepCopy: { a: 1, b: { c: 200 } }
这就如同两个孪生兄弟,各自管理自己的财务,一方的消费不会影响另一方的资产。
浅拷贝的实现方式
-
Object.assign({}, x)
这个方法以上案例使用过,主要对于对象的第一层复制到目标对象,嵌套关系的对象仍然是引用关系
由于前文已通过 Object.assign () 示例展示过浅拷贝特性,此处不再重复举例
-
Array.slice(),Array.concat()
let originalArray = [1, { a: 2 }]
const slicedArray = originalArray.slice()
const concatenatedArray = [].concat(originalArray)
-
扩展运算符(...)
适用于对象和数组
const originalObj = { a: 1, b: { c: 2 } }
const copy = { ...originalObj }
console.log(copy) //{ a: 1, b: { c: 2 } }
const arr = [1, 2, 3]
const clone = [...arr]
console.log(clone) // [1, 2, 3]
深拷贝实现方式
-
JSON.parse(JSON.stringify()):简单粗暴的深拷贝方法
先将对象转换为 JSON 字符串,再将字符串解析为新对象,从而实现深拷贝。不过,这个方法有一些局限性,它不能处理函数、undefined、Symbol 等数据类型,
const original = { a: 1, b: { c: 2 } }
const deepCopy = JSON.parse(JSON.stringify(original))
-
递归实现深拷贝
const obj = {
uname: 'Jeremy',
age: 18,
hobby: ['篮球', '健身'],
family: {
baby: '小jeremy'
}
}
const o = {}
// 拷贝函数
function deepCopy(newObj, oldObj) {
for (let k in oldObj) {
// 处理数组的问题 一定先写数组 在写 对象 不能颠倒
if (oldObj[k] instanceof Array) {
newObj[k] = []
// newObj[k] 接收 [] hobby
// oldObj[k] ['篮球', '健身']
deepCopy(newObj[k], oldObj[k])
} else if (oldObj[k] instanceof Object) {
newObj[k] = {}
deepCopy(newObj[k], oldObj[k])
}
else {
// k 属性名 uname age oldObj[k] 属性值 18
// newObj[k] === o.uname 给新对象添加属性
newObj[k] = oldObj[k]
}
}
}
deepCopy(o, obj)// 函数调用 两个参数 o 新对象 obj 旧对象
应用场景
浅拷贝:简单便捷的轻量级复制
浅拷贝适用于只需要复制对象的一层属性,且不关心嵌套对象的修改会影响原对象的场景。例如,在一些简单的数据展示场景中,只需要对数据进行简单的复制和展示,不需要对数据进行深度修改。就像在一个展览中,我们只需要展示物品的外观,而不需要关心物品内部的结构是否被修改。
深拷贝:安全可靠的深度复制
深拷贝则适用于需要完全独立的对象副本,避免修改新对象影响原对象的场景。例如,在进行数据备份、复杂数据结构的修改操作时,深拷贝可以确保数据的独立性和安全性。就像在银行进行资金备份时,我们需要确保备份的数据和原始数据完全独立,不受任何修改的影响。
最后
总之,掌握 JavaScript 中深浅拷贝的区别和实现方式,能够让我们在编程时更加灵活地处理数据,避免因数据复制不当而引发的各种问题。希望本文的介绍能帮助你在实际开发中做出正确的选择,让你的代码更加健壮和高效。