当前位置: 首页 > news >正文

前端深拷贝与浅拷贝(附实现方法)

理解

都是对原本的对象进行一份复制,差异如下:

  • 浅拷贝
    创建一个新对象,如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 。这个内存地址指向同一个堆内存,如果其中一个对象改变了堆内存存放的值,那么所有的对象都会受到影响。

  • 深拷贝
    创建一个新对象,如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,则从堆内存中开辟一个新的区域存放该引用类型指向的堆内存中的值,且修改新对象的值不会影响原对象。

放两张我做的蹩脚图加深一下理解:
在这里插入图片描述
在这里插入图片描述

总而言之,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

代码实现

浅拷贝实现方法

  1. 展开运算符…
    ES6语法,可以非常方便的实现浅拷贝。
const obj1 = {
      name: "icy",
      age: 20,
      hobbies: ["eat", "sleep", "game"],
    };
    const obj2 = { ...obj1 };
    console.log(obj2);

输出:
在这里插入图片描述
为了验证是浅拷贝,我们改变一下obj中数组的第一项的值,然后再输出ojb1:

const obj1 = {
      name: "icy",
      age: 20,
      hobbies: ["eat", "sleep", "game"],
    };
    const obj2 = { ...obj1 };
    //修改堆内存中的值
    obj2.hobbies[0] = "play";

    console.log("修改后obj2", obj2);
    console.log("修改后obj1", obj1);

输出结果:
在这里插入图片描述
obj1和obj2都受到了影响,验证了浅拷贝。

  1. Object.assign() 方法
    该方法用于合并对象,用法 object.assign(合并的对象,合并的对象…)
    支持传入多个对象,用逗号分隔,返回值为合并后的新对象
  const obj1 = {
      name: "icy",
      age: 20,
      hobbies: ["eat", "sleep", "game"],
    };
    //将拷贝对象与{}空对象合并
    const obj2 = Object.assign({}, obj1);
    console.log(obj2);
  1. 函数库lodash的_.clone方法
    lodash中文文档:https://www.lodashjs.com/
    需要node环境,下载npm包。
    全局安装:
    npm i -g npm
    安装到依赖中:
    npm i --save lodash
import _ from "lodash";  //导入ladash包

const obj1 = {
  name: "icy",
  age: 20,
  hobbies: ["eat", "sleep", "game"],
};
const obj2 = _.clone(obj1);
console.log(obj2);
console.log(obj1.hobbies === obj2.hobbies); //true

结果如下:
在这里插入图片描述

  1. 数组合并方法 concat()
    该方法用于数组合并,合并的数组.concat(被合并的数组…)
    参数可有多个,用逗号分隔,返回合并后的数组。
    原理:用原数组去合并一个空数组,返回合并后的数组。
const arr1 = [ 1,2,3,{name:'icy',gender:'美少女']
const arr2 = arr1.concat()
  1. 数组剪裁方法 slice()
    该方法用途很多,可对数组进行增删,剪裁操作。
    mdn上slice介绍:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
const arr1 = [ 1,2,3,{name:'icy',gender:'美少女']
const arr2 = arr1.slice() //返回剪裁后的数组,这里没有剪裁掉任何项,相当于返回原数组

深拷贝的实现方式

  1. JSON.parse(JSON.stringify())
    利用JSON.stringfy()将对象转为json格式的字符串,再利用JSON.parse()将json格式的字符串转为对象,转换后的对象是一个新对象。
 const obj1 = {
      name: "icy",
      age: 22,
      gender: "美少女",
      hobbies: ["eat", "sleep", "game"],
    };
    const obj2 = JSON.parse(JSON.stringify(obj1)); //深拷贝后的对象
    console.log(obj2);

输出:
在这里插入图片描述
验证一下深拷贝:
改变obj2的hobbies[0],看下对obj1是否有影响:

    obj2.hobbies[0] = "看动漫";
    console.log("obj1===", obj1);
    console.log("obj2===", obj2);

在这里插入图片描述
结论:没有影响,深拷贝成功。

  • 注意点:
    这种方法虽然简单方便,可以实现数组或对象的深拷贝。但不能处理函数正则,因为这两者基于JSON.stringify和JSON.parse处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null)了。
    比如:
 const obj1 = {
      name: "icy",
      age: 22,
      gender: "美少女",
      hobbies: ["eat", "sleep", "game"],
      //函数
      watchComic: () => {
        console.log("icy 我不做人啦");
      },
      //正则
      regx: /^icy{3}$/g,
    };
    const obj2 = JSON.parse(JSON.stringify(obj1)); 
    console.log("obj2===", obj2);

输出:
在这里插入图片描述

函数没了,正则变为空对象。

  1. 函数库lodash的_.cloneDeep方法
    lodash提供了cloneDeep方法实现深拷贝
import _ from "lodash";
const obj1 = {
  name: "icy",
  age: 20,
  hobbies: ["eat", "sleep", "game"],
};
const obj2 = _.clone(obj1); //浅拷贝
console.log(obj2);
console.log(obj1.hobbies === obj2.hobbies); //true

const obj3 = _.cloneDeep(obj1); //深拷贝
console.log("obj3==", obj3); //false
console.log(obj1.hobbies === obj3.hobbies);

输出结果对比:
在这里插入图片描述
3. jQuery.extend()方法
jQuery提供了extend方法可以实现深拷贝。

//第一个参数为true,就是深拷贝,为false则是浅拷贝
$.extend(deepCopy, target, object1, [objectN])

import $ from "jquery"
const obj1 = {
  name: "icy",
  age: 20,
  hobbies: ["eat", "sleep", "game"],
};
var obj2 = $.extend(true, {}, obj1);
  1. 手写递归方法

递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。

下面是大佬写的,考虑到了,Date,正则,函数,对象循环引用(自己引用自己)的一个深拷贝递归:

 function deepClone(obj, hash = new WeakMap()) {
      if (obj === null) return obj; // 如果是null或者undefined就不进行拷贝操作
      if (obj instanceof Date) return new Date(obj);
      if (obj instanceof RegExp) return new RegExp(obj);
      // 可能是对象(包括函数)或者普通的值  如果是函数或者普通的值不需要深拷贝
      if (typeof obj !== "object") return obj;
      // 是对象的话就要进行深拷贝
      if (hash.get(obj)) return hash.get(obj);

      let cloneObj = new obj.constructor();
      // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
      hash.set(obj, cloneObj);

      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          // 实现一个递归拷贝
          cloneObj[key] = deepClone(obj[key], hash);
        }
      }
      return cloneObj;
    }

    let obj = { name: 1, address: { x: 100 } };
    obj.o = obj; // 对象存在循环引用的情况
    let d = deepClone(obj);
    obj.address.x = 200;
    console.log(d);

参考文章:https://segmentfault.com/a/1190000020255831
https://juejin.cn/post/6844904197595332622

相关文章:

  • C#工业生产线MES系统,源代码分享
  • 2022软考高项十大领域知识整理(四)-人力资源管理、干系人管理、采购管理
  • 企业级低代码平台Jeecgboot3.4.2及3.4.3版本新功能介绍
  • SQL SERVER Sequence Number 序列号
  • 【Web实战-Tomcat-Servlet-Thymeleaf -JDBC-MySQL】浏览器页面显示数据库数据(水果库存系统)
  • 【超好懂的比赛题解】2022CCPC四川省赛 个人题解
  • Pytest如何执行txt格式的文本测试
  • 目标检测算法——YOLOv5 结合Swin Transformer V2
  • Web3究竟红在哪里,它的出现能为人类社会带来什么?
  • 【学姐面试宝典】前端基础篇Ⅴ——JS深浅拷贝、箭头函数、事件监听等
  • 什么是web3.0
  • Keras深度学习实战(31)——构建电影推荐系统
  • 高薪程序员面试题精讲系列157之面试中的那些“黑话”,你知道多少?
  • 适合前端开发的VS Code插件
  • 计算机毕业设计node+vue基于微信小程序的西餐外卖系统 uniapp 小程序
  • 《Javascript数据结构和算法》笔记-「字典和散列表」
  • Bytom交易说明(账户管理模式)
  • CNN 在图像分割中的简史:从 R-CNN 到 Mask R-CNN
  • css系列之关于字体的事
  • emacs初体验
  • fetch 从初识到应用
  • go append函数以及写入
  • IDEA常用插件整理
  • js递归,无限分级树形折叠菜单
  • js学习笔记
  • leetcode讲解--894. All Possible Full Binary Trees
  • Nginx 通过 Lua + Redis 实现动态封禁 IP
  • Node.js 新计划:使用 V8 snapshot 将启动速度提升 8 倍
  • Terraform入门 - 3. 变更基础设施
  • 翻译--Thinking in React
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 简单易用的leetcode开发测试工具(npm)
  • 什么是Javascript函数节流?
  • 微信开放平台全网发布【失败】的几点排查方法
  • 微信小程序实战练习(仿五洲到家微信版)
  • Salesforce和SAP Netweaver里数据库表的元数据设计
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • ​软考-高级-信息系统项目管理师教程 第四版【第14章-项目沟通管理-思维导图】​
  • #14vue3生成表单并跳转到外部地址的方式
  • (Bean工厂的后处理器入门)学习Spring的第七天
  • (BFS)hdoj2377-Bus Pass
  • (附源码)ssm旅游企业财务管理系统 毕业设计 102100
  • (九)One-Wire总线-DS18B20
  • .bat批处理(五):遍历指定目录下资源文件并更新
  • .net core webapi 大文件上传到wwwroot文件夹
  • .Net Core缓存组件(MemoryCache)源码解析
  • .NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划
  • .NET Project Open Day(2011.11.13)
  • .NET 编写一个可以异步等待循环中任何一个部分的 Awaiter
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件
  • .NET/C# 如何获取当前进程的 CPU 和内存占用?如何获取全局 CPU 和内存占用?
  • .NET开源项目介绍及资源推荐:数据持久层 (微软MVP写作)
  • .net连接MySQL的方法
  • /etc/skel 目录作用
  • [ IO.File ] FileSystemWatcher