摘要:对象拷贝,简而言之就是将对象再复制一份,但是,复制的方法不同将会得到不同的结果。

本文分享自华为云社区《js对象深浅拷贝,来,试试看!》,作者: 北极光之夜。。

一.速识概念:

对象拷贝,简而言之就是将对象再复制一份,但是,复制的方法不同将会得到不同的结果。比如直接给新变量赋值为一个对象:

  // 1.建一个对象var obj = {name: "北极光之夜。",like: "aurora",};// 2. 直接将对象赋值给变量 clonevar clone = obj;// 3.修改obj的like属性obj.like = "wind";// 4.输出 clone 对象console.log(clone);

从输出结果可以看到,我明明改变的是 obj 对象的属性,但是 clone 对象的属性也改变了。这是因为,当创建 obj 对象时,它在堆内存中开辟了一块空间存储对象的内容。而当 clone 直接赋值为 obj 时,clone 并不会再重新开辟一块堆内存,而是 obj 跟 clone 说我把我这内存空间存储的对象的地址给你,这个地址存在栈内存中,你通过栈内存的地址找到堆内存里对象的内容,咱们共用就完事了。所以说, obj 和 clone 指向的都是同一块内容,不管谁改了对象的内容,别人再访问都是改过之后的了。

所以这不是我们想要的,我不想共用,我想要属于自己的一片天地,我命由我不由你,所以这就需要浅拷贝和深拷贝了。

简单补充: 像一些基本数据类型的变量(Number Boolean String undefined null)被赋值时会直接在栈内存中开辟出了一个新的存储区域用来存储新的变量,不会如对象那样只是把引用给别人。

二.浅拷贝原理与常用方法:

简单来说浅拷贝就是只拷贝一层。什么意思呢 ?比如我有一个对象 obj :

   var obj = {name: "北极光之夜。",like: "aurora",};

我要把它拷贝给变量 b ,原理就是我再重新开辟一块内存,然后我直接看 obj 里有什么属性和值就直接复制一份,比如通过如下方式实现:

       // 1.建一个对象var obj = {name: "北极光之夜。",like: "aurora",};// 2. 封装一个函数,实现传入一个对象返回一个拷贝后的新对象function cloneObj(obj) {let clone = {};// 3.用 for  in 遍历obj的属性for (let i in obj) {clone[i] = obj[i];}return clone;}// 4.执行函数,将得到一个新对象var clone = cloneObj(obj);// 5.更改 obj 属性值obj.like = "wind";// 6.输出console.log(clone);

结果:

可以看到,就是新建一个空对象,还是循环直接赋值给它,这时改变 obj 的like属性值 ,新建的那个对象也不受影响了。但是,如果 obj 是下面这种形式的呢:

  var obj = {name: "北极光之夜。",like: "aurora",num: {a: "1",b: "2",},};

此时再用上面那种方法就不行了,如果obj只改变像 name 这种属性还没问题,但是当 obj 改变得是像 num 这种引用类型(对象、数组都是引用类型)的数据时,拷贝的对象还是能被影响,因为浅拷贝只能拷贝一层,如果拷贝的对象里还有子对象的话,那子对象拷贝其是也只是得到一个地址指向而已。这通过上面代码也能看出,就一层循环而已。想要真的达到我命由我不由天的话得用深拷贝,真正的刨根问底。深拷贝见第三大点。下面介绍下浅拷贝常用的方法,当对象只有一层的时候还是用浅拷贝好。

浅拷贝常用的方法:

1.第一种是主要利用 for in 遍历原对象的属性。

// 封装一个函数,实现传入一个对象返回一个拷贝后的新对象function cloneObj(obj) {let clone = {};// 用 for  in 遍历obj的属性for (let i in obj) {clone[i] = obj[i];}return clone;}

2.可以用Object.keys()方法:

Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组。

 function cloneObj(obj) {let clone = {};for (let i of Object.keys(obj)) {clone[i] = obj[i];}return clone;}

3.可以用Object.entries()方法:

Object.entries()方法返回一个给定对象自身可枚举属性的键值对数组。

  function cloneObj(obj) {let clone = {};for (let [key, value] of Object.entries(obj)) {clone[key] = value;}return clone;}

4.可用Object.getOwnPropertyNames()配合forEach循环:

Object.getOwnPropertyNames()返回一个由它的属性构成的数组。

  function cloneObj(obj) {let clone = {};Object.getOwnPropertyNames(obj).forEach(function (item) {clone[item] = obj[item];});return clone;}

5.可用Object.defineProperty()方法:

Object.defineProperty(obj, prop, descriptor) 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。obj要定义属性的对象。prop要定义或修改的属性的名称或 Symbol。descriptor要定义或修改的属性描述符。

Object.getOwnPropertyDescriptor():返回指定对象上一个自有属性对应的属性描述符。
属性描述符:JS 提供了一个内部数据结构,用来描述对象的值、控制其行为。称为属性描述符。

 function cloneObj(obj) {let clone = {};Object.getOwnPropertyNames(obj).forEach(function (item) {// 获取原本obj每个属性修饰符var des = Object.getOwnPropertyDescriptor(obj, item);// 把属性修饰符赋值给新对象Object.defineProperty(clone, item, des);});return clone;}

还有很多方法,就不一一列举了

三.深拷贝常见方法:

深拷贝就不会像浅拷贝那样只拷贝一层,而是有多少层我就拷贝多少层,要真正的做到全部内容都放在自己新开辟的内存里。可以利用递归思想实现深拷贝。

1.可以如下实现,还是用 for in 循环,如果为属性对象则递归:

  function cloneObj(obj) {let clone = {};for (let i in obj) {// 如果为对象则递归更进一层去拷贝if (typeof obj[i] == "object" && obj[i] != null) {clone[i] = cloneObj(obj[i]);} else {clone[i] = obj[i];}}return clone;}

试一试看:

 // 1.建一个对象var obj = {name: "北极光之夜。",like: "aurora",age: {a: 1,b: 2,},};// 2. 封装一个函数,实现传入一个对象返回一个拷贝后的新对象function cloneObj(obj) {let clone = {};for (let i in obj) {// 如果为对象则递归更进一层去拷贝if (typeof obj[i] == "object" && obj[i] != null) {clone[i] = cloneObj(obj[i]);} else {clone[i] = obj[i];}}return clone;}// 4.执行函数,将得到一个新对象var clone = cloneObj(obj);// 5.更改 obj 属性值obj.age.a = "666";// 6.输出console.log(clone);

结果如下,拷贝成功,原对象改变无法使新对象也改变:

2.如果对象里面有数组怎么办,数组也跟对象一样是引用类型,那么我们可以在开头加个判断它是对象还是数组,数组的话赋空数组,一样遍历拷贝:

   function cloneObj(obj) {// 通过原型链判断 obj 是否为数组if (obj instanceof Array) {var clone = [];} else {var clone = {};}for (let i in obj) {// 如果为对象则递归更进一层去拷贝if (typeof obj[i] == "object" && obj[i] != null) {clone[i] = cloneObj(obj[i]);} else {clone[i] = obj[i];}}return clone;}

试一试看:

 var obj = {name: "北极光之夜。",like: "aurora",age: {a: [1, 2, 3],b: 2,},};// 2. 封装一个函数,实现传入一个对象返回一个拷贝后的新对象function cloneObj(obj) {// 先判断 obj 是否为数组if (obj instanceof Array) {var clone = [];} else {var clone = {};}for (let i in obj) {// 如果为对象则递归更进一层去拷贝if (typeof obj[i] == "object" && obj[i] != null) {clone[i] = cloneObj(obj[i]);} else {clone[i] = obj[i];}}return clone;}// 4.执行函数,将得到一个新对象var clone = cloneObj(obj);// 5.更改 obj 属性值obj.age.a[1] = "666";// 6.输出console.log(clone);

结果没问题:

当然,也可用Array.isArray(obj)方法用于判断一个对象是否为数组。如果对象是数组返回 true,否则返回 false。

 function cloneObj(obj) {// 判断 obj 是否为数组if (Array.isArray(obj)) {var clone = [];} else {var clone = {};}for (let i in obj) {// 如果为对象则递归更进一层去拷贝if (typeof obj[i] == "object" && obj[i] != null) {clone[i] = cloneObj(obj[i]);} else {clone[i] = obj[i];}}return clone;}

四.总结:

以上就是深浅拷贝的大致内容啦。因为对象是引用类型,所以直接赋值对象给新变量,那么新变量指向的内存和原对象是一样的。所以我们通过浅拷贝和深拷贝实现开辟自己的内存空间。而浅拷贝只拷贝一层,深拷贝拷贝全部。如果,文章有什么错误的,恳请大佬指出。

点击关注,第一时间了解华为云新鲜技术~

JS对象拷贝:深拷贝和浅拷贝相关推荐

  1. JavaScript 中的对象拷贝(深拷贝、浅拷贝)

    对象是 JavaScript 的基本块.对象是属性的集合,属性是键值对.JavaScript 中的几乎所有对象都是位于原型链顶部 Object 的实例. 介绍 如你所知,赋值运算符不会创建一个对象的副 ...

  2. 一文搞懂前端对象的深拷贝与浅拷贝

    在前端开发过程中常常会听到对象的深拷贝与浅拷贝,对于初学者来说,可能是傻傻的分不清楚,本人将详细介绍javascript中对象的深拷贝与浅拷贝. 一.javascript中的数据类型 基础数据类型 字 ...

  3. js中的深拷贝和浅拷贝区别

    js中的深拷贝和浅拷贝与值传递和引用传递有着些许的关联,都是根据堆栈中数据的储存来传递数据. 下面主要说一下我对深拷贝和浅拷贝的理解: 简单举个例子来说明:当我们声明一个a变量并且赋值,并且让b等于a ...

  4. python深拷贝一个对象_Python对象的深拷贝和浅拷贝详解

    本文内容是在<Python核心编程2>上看到的,感觉很有用便写出来,给大家参考参考! 浅拷贝 首先我们使用两种方式来拷贝对象,一种是切片,另外一种是工厂方法.然后使用id函数来看看它们的标 ...

  5. Python基础:对象的深拷贝和浅拷贝的区别

    Python基础:对象的深拷贝和浅拷贝的区别 1 变量与对象 2 不可变对象与可变对象 3 直接赋值 4 浅拷贝 5 深拷贝 参考文献 1 变量与对象 对象:内存中存储数据的实体,有明确的类型.在Py ...

  6. java 复制Map对象(深拷贝与浅拷贝)

    java 复制Map对象(深拷贝与浅拷贝) CreationTime--2018年6月4日10点00分 Author:Marydon 1.深拷贝与浅拷贝 浅拷贝:只复制对象的引用,两个引用仍然指向同一 ...

  7. 浅谈对象的深拷贝和浅拷贝

    浅谈对象的深拷贝和浅拷贝 一.为什么使用对象的拷贝? 1.在普通数据类型赋值 let a=10let b=aa=20console.log(a)//a=20console.log(a)//b=10 因 ...

  8. Js中对象的深拷贝和浅拷贝

    浅拷贝:只拷贝对象的基础属性值,对属性值为对象或数组的属性则拷贝指针.  深拷贝:拷贝对象的所有属性作为一个全新的对象.拷贝前后的对象互不影响. 浅拷贝仅仅是指向被复制的内存地址,如果原地址中对象被改 ...

  9. 重新梳理下js中的深拷贝和浅拷贝

    参考链接: http://www.cnblogs.com/st-les... https://blog.csdn.net/hj7jay/... 浅拷贝: 1.最简单的浅拷贝就赋值. 由于js中的对象都 ...

最新文章

  1. 吐血总结|史上最全的MySQL学习资料!!
  2. 洛谷 1379 八数码难题
  3. 两个C++毫秒级定时器
  4. 【Python】HackBack(获取暴力破解服务器密码的IP来源)
  5. 关于Django中JsonResponse返回中文字典编码错误的解决方案
  6. 开源数据分析工具 CyberChef
  7. oracle goldengate实施简明介绍
  8. python支持向量机回归_Python机器学习之支持向量机——非线性SVC
  9. 制作chrome插件
  10. 数据库-Oracle【Oracle 三种集合数据类型的比较 】
  11. java myqq ui,MYQQ JAVA语言开发的QQ聊天系统,可以添加,删除,好友,支持在线,离线发送消 能 文 ICQ-IM-Chat 238万源代码下载- www.pudn.com...
  12. 第三方接入-飞猪酒店商品发布流程
  13. LSL学习笔记(3)
  14. [附源码]Java计算机毕业设计SSM电商直播订单管理系统
  15. 小程序拼团营销怎么做
  16. throws和throw的作用
  17. Pipeline流水线项目构建
  18. 抽象类和接口的区别(精简)
  19. python中text函数的语法_【01】Python基础语法
  20. 网络教育统考计算机计算总和,【重要】2018年教育部统考办1号文件,全国网络教育本科统考大变革...

热门文章

  1. (2)JavaScript书写语法
  2. JavaScript之注释规范化(JSDoc)
  3. 如何检测支付宝接口中notify_url.php有没有返回,微信小程序支付成功,但是notify_url接收不到回调如何排查此问题?...
  4. 计算机图形学二维图形基本变换实验原理,计算机图形学实验:二维图形变换.docx...
  5. 简书java前端_Java前端知识之JSP
  6. 东风畅行java_东风畅行载货车为何可以口碑很好吗?是配置高?或者另有原因?...
  7. java输入args不能为空_java程序入口为什么有的空括号在args前面有的?
  8. 12v电源正负极区分_解决冬天车辆无法启动的难题——车用应急启动电源选购要点及评测...
  9. c++利用初始化列表在类内部和类外部定义构造函数的区别
  10. python-opencv学习第二章