JavaScript 深拷贝与浅拷贝

发布于 2021-11-28 20:21:57 字数 2714 浏览 1185 评论 0

谈到深拷贝与浅拷贝首先要将数据类型的概念。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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84961 人气
更多

推荐作者

爱人如己

文章 0 评论 0

萧瑟寒风

文章 0 评论 0

云雾

文章 0 评论 0

倒带

文章 0 评论 0

浮世清欢

文章 0 评论 0

撩起发的微风

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文