JavaScript基础之--- 深拷贝与浅拷贝

 

理解深拷贝和浅拷贝之前,先来看一下JavaScript的数据类型。

 

1、基本类型和引用类型 

//案例1
var num1 = 1, num2 = num1;
console.log(num1) //1
console.log(num2) //1
num2 = 2; //修改num2
console.log(num1) //1
console.log(num2) //2
//案例2
var obj1 = {x: 1, y: 2}, obj2 = obj1;
console.log(obj1) //{x: 1, y: 2}
console.log(obj2) //{x: 1, y: 2}
obj2.x = 2; //修改obj2.x
console.log(obj1) //{x: 2, y: 2}
console.log(obj2) //{x: 2, y: 2}

 

ECMAScript 变量可能包含两种不同数据类型的值: 基本类型值 和 引用类型值。

 

基本类型值: 那些保存在栈内存中的简单数据段; 即这些值完全保存在内存中的一个位置。

引用类型值: 那些保存在堆内存中的对象;也就是说变量中保存的实际上是一个指针,这个指针指向内存中的另一个位置(保存对象)。

基本类型赋值等于在一个新的地方安装连锁店的规范标准新开一个分店,新开的店与其他旧店互不相关,各自运营;

而引用类型赋值相当于一个店有两把钥匙,交给两个老板同时管理,两个老板的行为都有可能对一间店的运营造成影响。

常见的基本数据类型: String、Number、Boolean、Null、Undefined、Symbol

常见的引用数据类型:Object、 Array、Function

 

2、深拷贝 与 浅拷贝

深拷贝 与 浅拷贝的概念只存在于 引用类型。

  • 浅拷贝:只能实现一维对象的复制;当对象是多维时(例如:二维数组或对象),改变二维数组的值会影响原数组【因为二维数组值复制的是其引用】。
  • 深拷贝:可以实现多维对象的复制。

(1)Array 和 Object 自身所带的深拷贝的方法

Array中具备深拷贝的方法: splice、concat、Array.from() ---  但是只能实现一维数组的深拷贝。

var arr1 = [1, 2], arr2 = arr1.slice();
console.log(arr1); //[1, 2]
console.log(arr2); //[1, 2]
arr2[0] = 3; //修改arr2
console.log(arr1); //[1, 2]
console.log(arr2); //[3, 2]

二维数组的情况下,arr2的改变也会同时改变arr1:

var arr1 = [1, 2, [3, 4]], arr2 = arr1.slice();
console.log(arr1); //[1, 2, [3, 4]]
console.log(arr2); //[1, 2, [3, 4]]
arr2[2][1] = 5; 
console.log(arr1); //[1, 2, [3, 5]]
console.log(arr2); //[1, 2, [3, 5]]

 


 Object.assign()也只能实现一维对象的深拷贝,并不能实现真正的深拷贝。

var obj1 = {x: 1, y: 2}, obj2 = Object.assign({}, obj1);
console.log(obj1) //{x: 1, y: 2}
console.log(obj2) //{x: 1, y: 2}
obj2.x = 2; //修改obj2.x
console.log(obj1) //{x: 1, y: 2}
console.log(obj2) //{x: 2, y: 2}

//二维对象 var obj1 = {x: 1, y: {  m: 1} }; var obj2 = Object.assign({}, obj1); console.log(obj1) //{x: 1, y: {m: 1}} console.log(obj2) //{x: 1, y: {m: 1}} obj2.y.m = 2; //修改obj2.y.m console.log(obj1) //{x: 1, y: {m: 2}} console.log(obj2) //{x: 1, y: {m: 2}}

 造成只能实现一维对象深拷贝的原因:第一层的属性确实实现了深拷贝,拥有了独立的内存,但更深的属性却仍然公用了地址。


 

(2)JSON.parse(JSON.stringify(obj)) ,可是实现二维对象的深拷贝:

var obj1 = {x: 1, y: {m: 1}
};
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj1) //{x: 1, y: {m: 1}}
console.log(obj2) //{x: 1, y: {m: 1}}
obj2.y.m = 2; //修改obj2.y.m
console.log(obj1) //{x: 1, y: {m: 1}}
console.log(obj2) //{x: 2, y: {m: 2}}

 

JSON.parse(JSON.stringify(obj)) 进行深拷贝是有局限性的;不能深拷贝还有 undefined、function、symbol值的对象。

var obj1 = {x: 1,y: undefined,z: function add(z1, z2) {return z1 + z2},a: Symbol("foo")
};
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj1) //{x: 1, y: undefined, z: ƒ, a: Symbol(foo)}
console.log(JSON.stringify(obj1)); //{"x":1}
console.log(obj2) //{x: 1}

 MDN文档中提示:undefined、任意的函数以及 symbol 值,

在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。

 

(3)利用递归 来彻底解决深拷贝的问题

function deepCopy(obj) {// 创建一个新对象let result = {}let keys = Object.keys(obj),key = null,temp = null;for (let i = 0; i < keys.length; i++) {key = keys[i];    temp = obj[key];// 如果字段的值也是一个对象则递归操作if (temp && typeof temp === 'object') {result[key] = deepCopy(temp);} else {// 否则直接赋值给新对象result[key] = temp;}}return result;
}
var obj1 = {x: {m: 1},y: undefined,z: function add(z1, z2) {return z1 + z2},a: Symbol("foo")
};
var obj2 = deepCopy(obj1);
obj2.x.m = 2;
console.log(obj1); //{x: {m: 1}, y: undefined, z: ƒ, a: Symbol(foo)}
console.log(obj2); //{x: {m: 2}, y: undefined, z: ƒ, a: Symbol(foo)}

 

(4)非常特殊的情况---- 循环引用拷贝

var obj1 = {x: 1, y: 2
};
obj1.z = obj1;
var obj2 = deepCopy(obj1);

此时如果调用刚才的deepCopy函数的话,会陷入一个循环的递归过程,从而导致爆栈。jquery的$.extend也没有解决。

 

解决这个问题: 判断一个对象的字段是否引用了这个对象 或  这个对象的任意父类即可。

function deepCopy(obj, parent = null) {// 创建一个新对象let result = {};let keys = Object.keys(obj),key = null,temp= null,_parent = parent;// 该字段有父级则需要追溯该字段的父级while (_parent) {// 如果该字段引用了它的父级则为循环引用if (_parent.originalParent === obj) {// 循环引用直接返回同级的新对象return _parent.currentParent;}_parent = _parent.parent;}for (let i = 0; i < keys.length; i++) {key = keys[i];temp= obj[key];// 如果字段的值也是一个对象if (temp && typeof temp=== 'object') {// 递归执行深拷贝 将同级的待拷贝对象与新对象传递给 parent 方便追溯循环引用result[key] = DeepCopy(temp, {originalParent: obj,currentParent: result,parent: parent});} else {result[key] = temp;}}return result;
}
var obj1 = {x: 1, y: 2
};
obj1.z = obj1;
var obj2 = deepCopy(obj1);
console.log(obj1); //太长了去浏览器试一下吧~ 
console.log(obj2); //太长了去浏览器试一下吧~ 

 

(5)当然也可以使用第三方库

   jquery的$.extend 和 lodash的_.cloneDeep 来解决深拷贝的问题。

 

3、总结

  • 简单的一维层次的拷贝可以利用数组自身方法和对象的Object.assign实现,在二维层次上方法失效,无法实现深拷贝
  • 简单粗暴的常见的拷贝可以通过JSON.parse(JSON.stringify(obj))实现,但对于属性的某些特殊类型的值失效。
  • 终极方法,用递归实现引用类型的深拷贝
  • 当然还有其他方法,比如使用第三方库内封装的方法

 

转载于:https://www.cnblogs.com/james23dong/p/9020771.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/367051.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue菜鸟从业记:完成项目最后一公里之真机测试和打包上线

最近我朋友王小闰他们公司的项目开发已经进入收尾阶段&#xff0c;前后端并行开发的差不多了&#xff0c;联调也调过了&#xff0c;上篇文章里也讲到了&#xff0c;所谓联调&#xff0c;就仿佛在说“我也不知道我的接口文档写的对不对&#xff0c;我们验证一下吧&#xff1f;我…

血淋淋的事实告诉你:你为什么不应该在JS文件中保存敏感信息

在JavaScript文件中存储敏感数据&#xff0c;不仅是一种错误的实践方式&#xff0c;而且还是一种非常危险的行为&#xff0c;长期以来大家都知道这一点。 而原因也非常简单&#xff0c;我们可以假设你为你的用户动态生成了一个包含API密钥的JavaScript文件&#xff1a; apiCall…

使用Infinispan进行Camel的事务性缓存

不久前&#xff0c;我为Camel 创建了Redis连接器。 Redis是很棒的键值存储&#xff08;还有更多&#xff09;&#xff0c;但是随后我需要一个在与Camel相同的JVM中运行的缓存&#xff0c;并注意到Infinispan已切换到ASL v2 。 Camel中已经有其他用于在JVM上进行缓存的连接器&am…

“景驰科技杯”2018年华南理工大学程序设计竞赛 A. 欧洲爆破(思维+期望+状压DP)...

题目链接&#xff1a;https://www.nowcoder.com/acm/contest/94/A 题意&#xff1a;在一个二维平面上有 n 个炸弹&#xff0c;每个炸弹有一个坐标和爆炸半径&#xff0c;引爆它之后在其半径范围内的炸弹也会爆炸&#xff0c;每个炸弹最多爆炸一次&#xff0c;每次随机选一个未引…

java 日期是否合法_检测日期字符串是否为合法(java版)

1 /**2 * 检测日期字符串是否为合法3 *paramdateStr4 *paramformat5 *return6 */7 public static final boolean checkDateFormat(String dateStr,String format) throwsNumberFormatException{8 if(dateStr null || "".equals(dateStr) ) return false;9 if(dateSt…

从零开始搭建一个vue.js的脚手架

在谷歌工作的时候&#xff0c;我们要做很多界面的原型&#xff0c;要求快速上手&#xff0c;灵活运用&#xff0c;当时用的一些现有框架&#xff0c;比如angular&#xff0c;太笨重了——尤雨溪&#xff08;Vue.js 作者&#xff09; vue.js是现在一个很火的前端框架&#xff0c…

更安全的Web通信HTTPS

1. HTTP协议存在的问题 阅读本篇需要对HTTP协议有最基本的了解。 借用《图解密码技术》里的图片&#xff0c;我们以如下一个购物场景开始介绍&#xff1a; 在网购过程中&#xff0c;如果使用纯粹的HTTP协议&#xff0c;那么用户的账号密码&#xff0c;信用卡&#xff0c;银行卡…

Java中的Memento设计模式-示例教程

记忆模式是行为设计模式之一 。 当我们要保存对象的状态以便以后可以恢复时&#xff0c;可以使用Memento设计模式。 记忆模式用于以这种方式实现该目的&#xff0c;即在对象外部无法访问该对象的已保存状态数据&#xff0c;从而保护了已保存状态数据的完整性。 Memento模式通过…

CSS入门指南——页面的水平居中

我们经常看到这样的网页&#xff0c;即内容水平居中在屏幕中间&#xff0c;左右留白。我们来给这样的布局起个名字——水平居中布局 其实要实现这样的布局十分简单&#xff0c;即给中间部分一个宽度&#xff0c;设置margin左右值为auto,如&#xff0c;中间部分class"main&…

python控制台执行代码字符串_编写Python脚本以使用控制台命令执行

研究是在底部&#xff0c;阅读之前。。。谢谢。在我必须编写一个运行SQL查询的Python脚本。我创建了一个主类&#xff0c;名为SQLQuery。每个SQLQuery实例表示一个查询。脚本的结构必须如下所示&#xff1a;class SQLQuery(object):def __init___(self, string_myQuery)...inst…

Linux Tools Quick Tutorial 学习记录

总体 书籍链接 find命令 查找大于多少的文件 find / -type f -size 5M | xargs ls -lh | awk {print $5, $9} | ls -lrt

mysql explain的使用

一、explain返回各列的含义&#xff1a; 1、table&#xff1a;显示这一行的数据是关于那张表的 2、type&#xff1a;重要的列&#xff0c;显示连接使用了何种类型&#xff0c;从最好到最差的连接类型为const、eq_reg、ref、range、index、ALL 3、possible_keys&#xff1a;显示…

使用 Canvas 生成公众号头图

熟悉“前端晚自修”的朋友们应该知道&#xff0c;我们每期的头图除了上面的文字随着每期变动以外&#xff0c;几乎是一模一样的&#xff08;因为太懒了~&#xff09;。这个头图虽然丑了一点&#xff0c;但是也还说的过去&#xff0c;毕竟是我倾尽毕生艺术细胞拼出来的&#xff…

通过快速Java和文件序列化加快速度

从Java的第一个版本开始&#xff0c;每天都有许多开发人员试图至少达到与C / C 一样好的性能。 JVM供应商正在通过实现一些新的JIT算法来尽力而为&#xff0c;但仍有许多工作要做&#xff0c;尤其是在我们如何使用Java方面。 例如&#xff0c;对象<->文件序列化有很多优…

Flask mysql 模版传参_Flask渲染Jinja2模板和传参

### Flask渲染Jinja2模板和传参&#xff1a;1. 如何渲染模板&#xff1a;* 模板放在templates文件夹下* 从flask中导入render_template函数。* 在视图函数中&#xff0c;使用render_template函数&#xff0c;渲染模板。注意&#xff1a;只需要填写模板的名字&#xff0c;不需要…

08 Spring框架 AOP (一)

首先我们先来介绍一下AOP&#xff1a; AOP&#xff08;Aspect Orient Programming&#xff09;&#xff0c;面向切面编程&#xff0c;是面向对象编程OOP的一种补充。 面向对象编程是从静态角度考虑程序的结构&#xff0c;面向切面编程是从动态的角度考虑程序运行过程。 AOP底层…

使用Spring 3.2的DeferredResult进行长轮询

在我们的最后一集中 &#xff0c; Agile Cowboys Inc.的首席执行官刚刚雇用了Java / Spring顾问&#xff0c;方法是为他提供最初为女友购买的保时捷。 这位首席执行官的女友因失去保时捷而感到不安&#xff0c;已将其婚外情告诉了他的妻子。 他的妻子在分拆了CEO的套房后已申请…

移动spa商城优化记(一)---首屏优化篇

背景 随着公司业务的不断壮大&#xff0c;最近老是有用户反应公司APP内的商城打开比较慢&#xff0c;这可不行啊&#xff0c;慢了容易流失用户&#xff0c;流失用户减少公司业绩&#xff0c;公司业绩少我的年终奖就少…………&#xff0c;所以为了公司&#xff0c;也为了自己&a…

hprose for java 教程_hprose for java源码分析-4

4.1 疑窦丛生书接上回。上回说到&#xff0c;从HproseClient.java ------------------------- (#0)invokeHandler.handle()开始&#xff0c;将经历一个漫长的调用过程&#xff0c;下面把整个调用链粘出来&#xff0c;先认识下这个庞然大物。( >>> 表示调用到&#xff…

Git可视化极简易教程 — Git GUI使用方法

Git可视化极简易教程 — Git GUI使用方法 学习了&#xff1a;http://www.runoob.com/w3cnote/git-gui-window.html转载于:https://www.cnblogs.com/stono/p/9026292.html