最近Vue项目中写到一个业务,就是需要把对话框的表单中的数据,每次点击提交之后,就存进一个el-table表格中,待多次需要的表单数据都提交进表格之后,再将这个表格提交,实现多个表单数据的同时提交,期间还可以用表格进行预览、修改等其他操作。将每个表单数据存进表格的代码大致代码如下:

    let object=this.ruleForm;

    this.tableData.push(object);

  其中,对话框中的表单使用了el-form,this.ruleForm是vue实例中的一个对象,而this.tableData是vue实例中的一个数组对象。直接将this.ruleForm赋值给一个变量object,然后每次再push进this.tableData里,这样看上去逻辑似乎也没啥毛病,但是,这样就会产生一个神奇的现象:每次填写表单中的数据的时候,表格中的每一行数据都会随着你表单的填写的改变而改变。

  这里就是出现了题目所谈到的问题,涉及到了js对象的直接赋值、浅拷贝与深拷贝。

直接赋值

  把一个对象a赋值给一个对象b相当于把一个对象b的地址指向对象a的地址,所以,他们实际上是同一个对象。由于这个项目是Vue,这次的问题就出现在了直接赋值上,Vue的响应式会让你更直观的知道他们的实质。以图1直接赋值的例子,person对象中有两个属性,一个是name,一个是对象属性ageAndSex;为什么要弄一个对象属性,这个会涉及到后面的浅拷贝和深拷贝问题,这也是他们之间的区别。由于内存地址我们很难监测到,但是我们可以通过严格相等运算符"==="来检测二者是否指向同一个地址。

图1 如果二者都是对象,严格相等运算符则会去检查它们是否指向相同的内存地址。

  以刚才的例子为例,如图2所示。刚开始的时候给personCopy的name属性赋值小刚,发现,person也发生了改变。给personCopy的对象属性ageAndSex的age属性赋值17,person也发生了改变。即:直接赋值,修改赋值后的对象b的非对象属性,也影响原对象a的非对象属性;修改赋值后的对象b的对象属性,也影响原对象a的对象属性

图2 直接赋值

浅拷贝

  浅拷贝只会赋值制对象的非对象属性,不会指向同一个地址。ES6中有个浅拷贝的方法Object.assign(target, ...sources)。以之前直接赋值的对象为例,如图3所示。

图3 浅拷贝,赋值的对象与被复制的对象不会指向同一个地址

  修改赋值后的对象b的非对象属性,不会影响原对象a的非对象属性;修改赋值后的对象b的对象属性,却影响原对象a的对象属性,如图4所示。

图4 浅拷贝

  es6中还有一个扩展运算符"..."也可以实现浅拷贝,还是以之前的对象为例,可以写成这种形式:var personCopy= { ...person };如图5所示。

图5 扩展运算符实现浅拷贝(赋值"小刚"等的操作与之前的结果完全相同,就不全贴出来了)

  考虑到es6的支持程度,如果你的项目不支持es6,但是又想实现浅拷贝的话,也可以尝试js原生的concat方法。但由于concat只能操作数组,所以需要先将person封装为一个对象数组,写成这种形式:

    var person=[{name:"小明",ageAndSex:{age:16,sex:"男"}}];

    var personCopy=[].concat(person);

如图6所示,到时想得到person对象的时候var personCopyObjet=pesronCopy[0]即可。

图6 concat方法实现浅拷贝

深拷贝

  深拷贝会另外拷贝一份一个一模一样的对象,但是不同的是会从堆内存中开辟一个新的区域存放新对象,新对象跟原对象不再共享内存,修改赋值后的对象b不会改到原对象a。即深拷贝,修改赋值后的对象b的非对象属性,不会影响原对象a的非对象属性;修改赋值后的对象b的对象属性,也不会影响原对象a的对象属性。而且,二者不指向同一个对象。

  很明显,深拷贝比较符合我这次的业务需求。深拷贝,比较笨一点的办法就是将自己需要的数据自己封装起来。

      let object={
                           repayment:this.ruleForm.repayment,
                           interestType:this.ruleForm.interestType,
                           productDeadline:this.ruleForm.productDeadline,
                           circumstancesOfDetention:this.ruleForm.circumstancesOfDetention,
                           }
                      this.tableData.push(object);

  但是,这样明显会使代码很臃肿,而且,这还是在需要的数据只有4条的情况下,如果这个object需要封装十几条非对象属性的情况下,明显结构不复杂的情况下,这种代码需要改进。

  有一种非常简单的方法就是序列化成为一个JSON字符串,将对象的内容转换成字符串的形式,再用JSON.parse()反序列化将JSON字符串变成一个新的对象,这样原对象就与复制后的新对象没了必然的关系。以前文提到的personCopy和person为例,写法如下:var personCopy=JSON.parse(JSON.stringify(person));如图7所示。

图7 深拷贝

  但是由于用到了JSON.stringify(),这也会导致一系列的问题,因为要严格遵守JSON序列化规则:原对象中如果含有Date对象,JSON.stringify()会将其变为字符串,之后并不会将其还原为日期对象。或是含有RegExp对象,JSON.stringify()会将其变为空对象,属性中含有NaNInfinity-Infinity,则序列化的结果会变成null,如果属性中有函数,undefined,symbol则经过JSON.stringify()序列化后的JSON字符串中这个键值对会消失,因为不支持。

  所以,这个时候笨的办法也是有好处的,就是面对一些特殊的类型,或是对象属性复杂的情况下,因为自己对程序的需求比较了解,就可以按照自己的需要进行封装。不管黑猫白猫,能抓到老鼠的就是好猫。

js对象的直接赋值、浅拷贝与深拷贝的更多相关文章

  1. 对Python中列表和数组的赋值,浅拷贝和深拷贝的实例讲解

    引用:https://www.jb51.net/article/142775.htm 列表赋值: 1 2 3 4 5 6 7 >>> a = [1, 2, 3] >>&g ...

  2. JS中的引用、浅拷贝和深拷贝

    js的深拷贝浅拷贝是很常遇到的问题,一直模模糊糊有点说不过去,所以这次好好总结一下. 1.js的引用 JS分为基础类型和引用类型两种数据类型: 基础类型:number.string.boolean.n ...

  3. 编写高质量代码改善C#程序的157个建议[为类型输出格式化字符串、实现浅拷贝和深拷贝、用dynamic来优化反射]

    前言 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议13.为类型输出格式化字符串 建议14.正确实现浅拷贝和深 ...

  4. 编写高质量代码改善C#程序的157个建议——建议14: 正确实现浅拷贝和深拷贝

    建议14: 正确实现浅拷贝和深拷贝 为对象创建副本的技术称为拷贝(也叫克隆).我们将拷贝分为浅拷贝和深拷贝. 浅拷贝 将对象中的所有字段复制到新的对象(副本)中.其中,值类型字段的值被复制到副本中后, ...

  5. C#程序编写高质量代码改善的157个建议【13-15】[为类型输出格式化字符串、实现浅拷贝和深拷贝、用dynamic来优化反射]

    前言 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议13.为类型输出格式化字符串 建议14.正确实现浅拷贝和深 ...

  6. C#浅拷贝与深拷贝测试

    1.浅拷贝与深拷贝 浅拷贝:只复制对象的基本类型,对象类型,仍属于原来的引用.       深拷贝:不紧复制对象的基本类,同时也复制原对象中的对象.就是说完全是新对象产生的. 2.浅拷贝与深拷贝的区别 ...

  7. python 进阶篇 浅拷贝与深拷贝

    阐述引用.浅拷贝和深拷贝前,首先需要要了解 Python 的世界里,一切皆对象,每个对象各包含一个 idendity.type 和 value. 引用(Reference) >>> ...

  8. JS 对象的深拷贝和浅拷贝

    转载于原文:https://www.cnblogs.com/dabingqi/p/8502932.html 这篇文章是转载于上面的链接地址,觉得写的非常好,所以收藏了,感谢原创作者的分享. 浅拷贝和深 ...

  9. js对象的浅拷贝与深拷贝

    浅拷贝和深拷贝都是对于JS中的引用类型而言的,浅拷贝就只是复制对象的引用(堆和栈的关系,原始(基本)类型Undefined,Null,Boolean,Number和String是存入堆,直接引用,ob ...

随机推荐

  1. Redis(五)持久化

    一.RDB RDB持久化是把当前进程数据生成快照保存到硬盘的过程,触发RDB持久化过程分为手动触发和自动触发. 1.触发机制 (1)手动触发:save命令和bgsave命令 save命令:阻塞当前Re ...

  2. 小白 Python 爬虫部署 Linux

    前言 前面国庆节的时候写过一个简易的爬虫. <Python 简易爬虫实战> 还没看过的同学可以先看一下,这只爬虫主要用来爬取各个博客平台的阅读量等数据,一直以来都是每天晚上我自己手动在本地 ...

  3. 【Java必修课】通过Value获取Map中的键值Key的四种方法

    1 简介 我们都知道Map是存放键值对<Key,Value>的容器,知道了Key值,使用方法Map.get(key)能快速获取Value值.然而,有的时候我们需要反过来获取,知道Value ...

  4. C和C++中的引用传递

    两种引用传递的定义方式 第一种 #include<stdio.h> void changeValue(int *a); int main(){ int a =1; changeValue( ...

  5. 来玩一局CS吗?UE4射击游戏的独立服务器构建

    前言   根据UE4官方文档的介绍,UE4引擎在架构时就已经考虑到了多人游戏的情景,多人游戏基于客户端-服务器模式(CS模式).也就是说,会有一个服务器担当游戏状态的主控者,而连接的客户端将保持近似的 ...

  6. Linux后台运行Jar方法

    原文地址:http://blog.csdn.net/c1481118216 https://blog.csdn.net/c1481118216/article/details/53010963 在li ...

  7. 第六篇 视觉slam中的优化问题梳理及雅克比推导

    优化问题定义以及求解 通用定义 解决问题的开始一定是定义清楚问题.这里引用g2o的定义. \[ \begin{aligned} \mathbf{F}(\mathbf{x})&=\sum_{k\ ...

  8. 安装requests遇到的坑

    通过pip install requests命令安装,报错,提示“SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed”.百思不得其解,后 ...

  9. postman-接口间数据传递

    接口间数据传递 在我们做接口测试过程中会经常碰到使用上一个接口返回数据的情况,jmeter中可通过正则表达式提取,postman中如何提取呢?我们来看实例,这里使用的同一个接口来演示. ​ 我们提取出 ...

  10. Vue的指令以及组件化开发

    一. 自定义指令 如何: 1. 创建指令 Vue.directive("指令名",{ inserted(elem){//指令所在的元素被加载到DOM树上后自动执行指令 //elem ...