JavaScript 深拷贝与浅拷贝
谈到深拷贝与浅拷贝首先要将数据类型的概念。js 中有两种类型的数据,基本类型和引用类型。基本类型有 undefined,null,String,Number,Boolean,Symbol,BigInt。引用类型有 Object,Array,Function,Date,Map,Math 等。为什么要在这里说这个呢?
深拷贝和浅拷贝都是针对引用类型的。因为基本类型在存储中都是直接存在栈中的,而引用类型的内容是存在堆中,栈中存放的是这个引用类型的地址,然后有个指针指向堆中的数据。
浅拷贝
浅拷贝(一层),仅仅是复制了引用,彼此之间的操作会互相影响。
深拷贝
深拷贝(多层),在堆中重新分配内存,不同的地址,相同的值,互不影响
浅拷贝实现
// 使用Object.assign解决
// 使用Object.assign(),你就可以没有继承就能获得另一个对象的所有属性,快捷好用。
// Object.assign 方法只复制源对象中可枚举的属性和对象自身的属性。
let obj = { a:1, arr:[2,3]};
let res = Object.assign({}, obj)
console.log(res.arr === obj.arr); // true,指向同一个引用
console.log(res === obj); // false
// 使用扩展运算符(…)来解决
let obj = { a:1, arr:[2,3]};
let res = {...obj};
console.log(res.arr === obj.arr); // true,指向同一个引用
console.log(res === obj); // false
深拷贝实现
// 开发中常用JSON序列化来进行深拷贝,很好用
// 可以通过 JSON.parse(JSON.stringify(object)) 来解决
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
// 但是该方法也是有局限性的:
// 1.会忽略 undefined
// 2.不能序列化函数(会忽略函数)
// 3.不能解决循环引用的对象(对象成环)
// 并且该函数是内置函数中处理深拷贝性能最快的。当然如果你的数据中含
// 有以上三种情况下,可以使用 lodash 的深拷贝函数。
// 拓展一下,MessageChannel(异步通信,一般用于多个web worker并
// 想要在两个web worker之间实现通信的时候)的postMessage传递的数据也是深拷贝的,
// 这和web worker的postMessage一样。而且还可以拷贝undefined和循环引用的对象(函数不行)。
function deepCopy(obj) {
return new Promise((resolve) => {
const {port1, port2} = new MessageChannel();
port2.onmessage = ev => resolve(ev.data);
port1.postMessage(obj);
});
}
deepCopy(obj).then((copy) => {
let copyObj = copy;
console.log(copyObj, obj)
console.log(copyObj == obj)
});
// 递归实现深拷贝(递归比较耗性能,容易造成栈溢出,所以有尾递归的诞生,
// 得看浏览器兼容性,理解尾递归得理解函数的调用帧,之后会专门有一篇讲这个。)
const deepClone = function (source, nullVal) {
if (!source || typeof source !== 'object') {
return source
}
const targetObj = source.constructor === Array ? [] : {}
for (const keys in source) {
if (source.hasOwnProperty(keys)) {
if (source[keys] && typeof source[keys] === 'object') {
targetObj[keys] = deepClone(source[keys])
} else {
// 传null防止undefined被过滤
if (nullVal === null) {
targetObj[keys] = source[keys] === undefined ? nullVal : source[keys]
} else {
targetObj[keys] = source[keys]
}
}
}
}
return targetObj
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论