JavaScript中浅拷贝和深拷贝的区别和实现
深拷贝和浅拷贝的区别
浅拷贝(shallow copy):只复制指向某个对象的指针,而不复制对象本身,新旧对象共享一块内存;
深拷贝(deep copy):复制并创建一个一摸一样的对象,不共享内存,修改新对象,旧对象保持不变。
var a = 25;
var b = a;
b = 10;
console.log(a);//25
console.log(b);//10
//浅拷贝
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = obj1;
obj2.b = 40;
console.log(obj1);// { a: 10, b: 40, c: 30 }
console.log(obj2);// { a: 10, b: 40, c: 30 }
//深拷贝
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };
obj2.b = 40;
console.log(obj1);// { a: 10, b: 20, c: 30 }
console.log(obj2);// { a: 10, b: 40, c: 30 }
传值与传址
上一篇博客说明了什么是内存中的堆、栈以及变量类型,实际上是为这篇服务的,就是为了更好的理解什么是“浅拷贝”和“深拷贝”。
基本类型与引用类型最大的区别实际就是传值与传址的区别。测试用例:
var a = [1,2,3,4,5];
var b = a;
var c = a[0];
alert(b);//1,2,3,4,5
alert(c);//1
//改变数值
b[4] = 6;
c = 7;
alert(a[4]);//6
alert(a[0]);//1
从上面我们可以得知,当我改变b中的数据时,a中数据也发生了变化;但是当我改变c的数据值时,a却没有发生改变。
这就是传值与传址的区别。
因为a是数组,属于引用类型,所以它赋予给b的时候传的是栈中的地址(相当于新建了一个不同名“指针”),而不是堆内存中的对象。而c仅仅是从a堆内存中获取的一个数据值,并保存在栈中。所以b修改的时候,会根据地址回到a堆中修改,c则直接在栈中修改,并且不能指向a堆内存中。
浅拷贝
前面已经提到,在定义一个对象或数组时,变量存放的往往只是一个地址。当我们使用对象拷贝时,如果属性是对象或数组时,这时候我们传递的也只是一个地址。因此子对象在访问该属性时,会根据地址回溯到父对象指向的堆内存中,即父子对象发生了关联,两者的属性值会指向同一内存空间。
var a = {
key1:"11111"
}
function Copy(p) {
var c = {};
for (var i in p) {
c[i] = p[i];
}
return c;
}
a.key2 = ['小辉','小辉'];
var b = Copy(a);
b.key3 = '33333';
alert(b.key1); //1111111
alert(b.key3); //33333
alert(a.key3); //undefined
a对象中key1属性是字符串,key2属性是数组。a拷贝到b,12属性均顺利拷贝。给b对象新增一个字符串类型的属性key3时,b能正常修改,而a中无定义。说明子对象的key3(基本类型)并没有关联到父对象中,所以undefined。
b.key2.push("大辉");
alert(b.key2); //小辉,小辉,大辉
alert(a.key2); //小辉,小辉,大辉
但是,若修改的属性变为对象或数组时,那么父子对象之间就会发生关联。从以上弹出结果可知,我对b对象进行修改,a、b的key2属性值(数组)均发生了改变。其在内存的状态,可以用下图来表示。
原因是key1的值属于基本类型,所以拷贝的时候传递的就是该数据段;但是key2的值是堆内存中的对象,所以key2在拷贝的时候传递的是指向key2对象的地址,无论复制多少个key2,其值始终是指向父对象的key2对象的内存空间。
深拷贝
或许以上并不是我们在实际编码中想要的结果,我们不希望父子对象之间产生关联,那么这时候可以用到深拷贝。既然属性值类型是数组和或象时只会传址,那么我们就用递归来解决这个问题,把父对象中所有属于对象的属性类型都遍历赋给子对象即可。测试代码如下:
function Copy(p, c) {
var c = c || {};
for (var i in p) {
if (typeof p[i] === 'object') {
c[i] = (p[i].constructor === Array) ? [] : {};
Copy(p[i], c[i]);
} else {
c[i] = p[i];
}
}
return c;
}
a.key2 = ['小辉','小辉'];
var b={};
b = Copy(a,b);
b.key2.push("大辉");
alert(b.key2); //小辉,小辉,大辉
alert(a.key2); //小辉,小辉
由上可知,修改b的key2数组时,没有使a父对象中的key2数组新增一个值,即子对象没有影响到父对象a中的key2。其存储模式大致如下:
如何实现深拷贝
1递归递归去复制所有层级属性。
function deepClone(obj){
let objClone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a=[1,2,3,4],
b=deepClone(a);
a[0]=2;
console.log(a,b);
2通过JSON去解析
let obj = {name:'fiona-SUN'};
let copyObj = JSON.parse(JSON.stringify(obj));
copyObj.name = 'fiona';
console.log(copyObj.name); // 'fiona'
console.log(obj.name); // 'fiona-SUN'
3 es6之展开Object.assign(拷贝obj的内容到一个新的堆内存,copyObj存储新内存的引用)
let obj = {name:'fiona-SUN'};
let copyObj = Object.assign({}, obj);
copyObj.name = 'fiona';
console.log(copyObj.name); // 'fiona'
console.log(obj.name); // 'fiona-SUN'
4 es6之展开运算符(仅用于数组)
let arr = [1,2,3];
let copyArr = [...obj];
copyArr[2] = 0;
console.log(copyArr[2]); // 0
console.log(arr[2]); // 2
JavaScript中浅拷贝和深拷贝的区别和实现的更多相关文章
- JavaScript中浅拷贝和深拷贝的区别
JavaScript数据类型 基础数据类型:保存在栈内存中的简单数据段 ,有undefined,boolean,number,string,null 引用数据类型:Array,object,Funct ...
- Java中浅拷贝和深拷贝的区别
浅拷贝和深拷贝的定义: 浅拷贝: 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.即对象的浅拷贝会对"主"对象进行拷贝,但不会复制主对象 ...
- Python中浅拷贝和深拷贝的区别总结与理解
单层浅拷贝 import copy a = 1 # 不可变数据类型 copy_a = copy.copy(a) print(id(a),id(copy_a)) # 内存地址相同 a = [1,2] # ...
- python中浅拷贝和深拷贝的区别
浅拷贝 可变类型浅拷贝copy函数就是浅拷贝,只对可变类型的第一层对象进行拷贝,对拷贝的对象开辟新的内存空间进行存储,不会拷贝对象内部的子对象可变类型:a = [1, 2, 3] b = [11, 2 ...
- 全面聊聊JavaScript的浅拷贝和深拷贝
一.背景 首先我们可以看下面这段简单的代码: var obj = {name:'程序猿',sex:'男'}; var arr = ['程序猿','程序媛']; var copyobj = o ...
- 关于JavaScript的浅拷贝和深拷贝
在 JS 中有一些基本类型像是Number.String.Boolean,而对象就是像这样的东西{ name: 'Larry', skill: 'Node.js' },对象跟基本类型最大的不同就在于他 ...
- javascript篇-浅拷贝与深拷贝
理解javascript 的浅拷贝与深拷贝,首先看一下js的数据类型: js有5种基本数据类型:undefined,null,boolean,number,string 还有一种复杂的数据类型(也叫引 ...
- C++浅拷贝和深拷贝的区别
C++浅拷贝和深拷贝的区别 2012-04-24 21:22 11454人阅读 评论(6) 收藏 举报 c++deleteclass编译器c c++默认的拷贝构造函数是浅拷贝 浅拷贝就是对象的数据成员 ...
- Python FAQ2:赋值、浅拷贝、深拷贝的区别?
在Python编程过程中,经常会遇到对象的拷贝,如果不理解浅拷贝和深拷贝的概念,你的代码就可能出现一些问题.所以,在这里按个人的理解谈谈它们之间的区别. 一.赋值(assignment) 在<P ...
随机推荐
- 【设计模式】不同设计模式体现IOC控制反转
使用过Spring的开发者应该都对IOC控制反转功能有所了解,最开始学习时应该都知道使用依赖注入来实现IOC的功能,本文来介绍使用IOC控制反转思想的几种设计模式. 依赖注入来实现IOC 注入依赖是I ...
- Powershell中显示隐藏文件
PS> Get-ChildItem -Path $home -Force PS> Get-ChildItem -Path $home -Hidden
- 【资料】Mac OS X 10.9虚拟机镜像及tools
原文链接 http://bbs.itheima.com/thread-141793-1-1.html 1.首先解压文件,两个同时选中,右键进行解压. 2.打开虚拟机VMware workstation ...
- IE6不兼容问题
IE6不兼容问题 一.选择器兼容问题 1.交集选择器从IE7以上兼容(div.special): 2.儿子选择器(>):IE7开始兼容,IE6不兼容. 3.序选择器(first ...
- layui 自定义表单验证的几个实例
*注:使用本方法请先引入layui依赖的layu.js和layui.css 1.html <input type="text" name="costbudget&q ...
- 《JavaScript高级程序设计》笔记:基本概念(三)
数据类型 ECMAScript中有5种简单数据类型(也称为基本数据类型):undefined,null,boolean,number和string. typeof typeof null会返回“obj ...
- 在php中使用对称加密DES3,开发银行卡绑定,实名验证……
对称加密:对称加密是一种数据加密算法,对一组数据的加密和解密都使用一样的密钥(key),可以有效保护金融数据,常见的对称加密有DES,3DES,AES.RC2.RC4.RC5. DES3: 对DES算 ...
- Spring学习之旅(五)极速创建Spring AOP java工程项目
编译工具:eclipse. 简单说一下,Spring AOP是干嘛的? 假设你创建了一群类:类A,类B,类C,类D.... 现在你想为每个类都增加一个新功能,那么该怎么办呢?是不是想到了为每个类增加 ...
- 转载------35岁开始转变的观念(干了这碗鸡汤再写代码保证没有bug)
作为一个标准的IT男,从一开始的一线开发,做到管理层,又因为喜爱开发,跳槽继续专注coding,一干就是10多年.有时候就想,这样一直学习,coding,如果能干到老也挺不错的! 不过生活的压力是不会 ...
- AndroidStudio安装、配置、测试
AndroidStudio安装.配置.测试(win7_64bit) 目录 1.概述 2.本文用到的工具 3.安装测试 4.模拟器安装.使用 5.常用配置 6.注事事项 7.相关博文 >>看 ...