文章同步更新在我的 Github 上,欢迎访问,如果读完觉得有收获,给个 ⭐️ 鼓励下!
日常业务开发中,经常会遇到拷贝数据的情况,特别是当一个页面中多个数据源交互比较多的话,如果不注意处理源数据与修改后的数据,就很容易导致一些未知的 bug。而对数据的处理中,最常用的就是直接拷贝一份新的数据,在新数据上做修改,避免影响原始数据。在 JS 里拷贝一份数据,有深拷贝和浅拷贝两种方式。
首先我们要了解 JS 基本数据类型与引用类型,基本数据类型存放于内存中的栈数据结构,而引用类型的数据是存放于堆中,然后在栈里通过一个指针指向堆中的内存地址。
所谓浅拷贝,就是对基本数据类型,复制它在栈中存储的值;对于引用类型复制它在栈中存储的指针。这种拷贝方式会导致引用类型数据共享同一个内存地址,这就意味着,你修改其中一方的数据,也就相应地修改了另一份数据。
所谓深拷贝,就是对于基本数据类型,同样复制它在栈中存储的值;而对于引用类型,并不复制指针,而是在堆内存中新开辟一块区域存储数据,新复制后的数据与原来的数据是完全隔离开的,互不影响。所以如果需要拷贝的源数据里包含引用类型,应该使用深拷贝。
浅拷贝对象的几种方式
第一种,通过 Object.assign:
const obj = {
name: '张三',
friends: ['A', 'B', 'C']
}
const copy = Object.assign(obj)
obj.name = '李四'
obj.friends.push('D')
console.log(copy.name) // 李四
console.log(copy.friends) // ['A', 'B', 'C', 'D']
第二种,通过 ES6 的扩展运算符 ...
const obj = {
name: '张三',
friends: ['A', 'B', 'C']
}
const copy = {...obj}
obj.name = '李四'
obj.friends.push('D')
console.log(copy.name) // 注意,这里依旧打印 '张三',对基本类型复制的是独立的值
console.log(copy.friends) // ['A', 'B', 'C', 'D']
深拷贝的方式
第一种(推荐),使用 lodash,licia 等库提供的方法。
// lodash
_.cloneDeep(obj)
// licia
cloneDeep(obj)
第二种,使用 JSON.parse(JSON.stringify()):
const obj = {
name: '张三',
friends: ['A', 'B', 'C']
}
const copy = JSON.parse(JSON.stringify(obj))
console.log(copy)
第三种,自己实现一个深拷贝:
function cloneDeep(target, map = new WeakMap()) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {}
if (map.get(target)) {
return map.get(target)
}
map.set(target, cloneTarget)
for (const key in target) {
cloneTarget[key] = cloneDeep(target[key])
}
return cloneTarget
} else {
return target
}
}
总结
- 浅拷贝对基本数据类型拷贝的是值,对引用类型拷贝的是指针
- 深拷贝是在堆内存中新开辟一块区域,拷贝出来的数据完全独立,互不影响