深拷贝和浅拷贝的区别

  浅拷贝(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中浅拷贝和深拷贝的区别和实现的更多相关文章

  1. JavaScript中浅拷贝和深拷贝的区别

    JavaScript数据类型 基础数据类型:保存在栈内存中的简单数据段 ,有undefined,boolean,number,string,null 引用数据类型:Array,object,Funct ...

  2. Java中浅拷贝和深拷贝的区别

    浅拷贝和深拷贝的定义: 浅拷贝: 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.即对象的浅拷贝会对"主"对象进行拷贝,但不会复制主对象 ...

  3. Python中浅拷贝和深拷贝的区别总结与理解

    单层浅拷贝 import copy a = 1 # 不可变数据类型 copy_a = copy.copy(a) print(id(a),id(copy_a)) # 内存地址相同 a = [1,2] # ...

  4. python中浅拷贝和深拷贝的区别

    浅拷贝 可变类型浅拷贝copy函数就是浅拷贝,只对可变类型的第一层对象进行拷贝,对拷贝的对象开辟新的内存空间进行存储,不会拷贝对象内部的子对象可变类型:a = [1, 2, 3] b = [11, 2 ...

  5. 全面聊聊JavaScript的浅拷贝和深拷贝

    一.背景      首先我们可以看下面这段简单的代码: var obj = {name:'程序猿',sex:'男'}; var arr = ['程序猿','程序媛']; var copyobj = o ...

  6. 关于JavaScript的浅拷贝和深拷贝

    在 JS 中有一些基本类型像是Number.String.Boolean,而对象就是像这样的东西{ name: 'Larry', skill: 'Node.js' },对象跟基本类型最大的不同就在于他 ...

  7. javascript篇-浅拷贝与深拷贝

    理解javascript 的浅拷贝与深拷贝,首先看一下js的数据类型: js有5种基本数据类型:undefined,null,boolean,number,string 还有一种复杂的数据类型(也叫引 ...

  8. C++浅拷贝和深拷贝的区别

    C++浅拷贝和深拷贝的区别 2012-04-24 21:22 11454人阅读 评论(6) 收藏 举报 c++deleteclass编译器c c++默认的拷贝构造函数是浅拷贝 浅拷贝就是对象的数据成员 ...

  9. Python FAQ2:赋值、浅拷贝、深拷贝的区别?

    在Python编程过程中,经常会遇到对象的拷贝,如果不理解浅拷贝和深拷贝的概念,你的代码就可能出现一些问题.所以,在这里按个人的理解谈谈它们之间的区别. 一.赋值(assignment) 在<P ...

随机推荐

  1. 46.Linux-创建rc红外遥控平台设备,实现重复功能(2)

    上章链接:46.Linux-分析rc红外遥控平台驱动框架,修改内核的NEC解码函数BUG(1) 在上章分析了红外platform_driver后,已经修改bug后,接下来我们自己创建一个红外platf ...

  2. 【Java每日一题】20170317

    20170316问题解析请点击今日问题下方的“[Java每日一题]20170317”查看(问题解析在公众号首发,公众号ID:weknow619) package Mar2017; public cla ...

  3. Hibernate入门(九)级联删除

    Hibernate级联删除 上一篇文章学习了级联保存和更新,这个级联删除应该很好理解的.一样的道理,删除一方,同时删除有关联的一方. https://www.cnblogs.com/deepSleep ...

  4. linux 中rc是什么意思

    在Linux中,最为常用的缩略语也许是"rc" 它是"runcomm"的缩写――即名词"run command"(运行命令)的简写.rc&q ...

  5. Java设计模式 - 单例模式详解(下)

    单例模式引发相关整理 关联线程安全 在多线程下,懒汉式会有一定修改.当两个线程在if(null == instance)语句阻塞的时候,可能由两个线程进入创建实例,从而返回了两个对象.对此,我们可以加 ...

  6. JS检测浏览器Adobe Reader插件

    Web应用中当我们希望向用户显示pdf文档时候,如果用户安装了Adobe Reader之类的pdf阅读器,就可以直接打开文档在浏览器中显示, 但是,当用户没有安装这类软件的时候,自然是打不开的,为了系 ...

  7. -moz、-ms、-webkit浏览器前缀解释(PS:后续在详细解释)

    -moz-是Firefox Gecko内核,moz代表的是Firefox的开发商Mozill -ms代表ie浏览器私有属性 -webkit代表safari.chrome私有属性

  8. Ne10编译安装

    介绍 NEON,即"ARM Advanced SIMD",是ARM从ARMv7开始提供的高级单指令多数据(SIMD)扩展.它是一种64/128位混合SIMD体系结构.NEON在网上 ...

  9. DES数据解密

    /// <summary> /// DES数据解密 /// </summary> /// <param name="targetValue">& ...

  10. C#生成唯一订单号

    今天系统出了一个问题,发现生成的订单号存在重复的情况了,这是要命的bug,不马上解决,就会有投诉了 经过改进后的代码我先简单的放一下,后面在慢慢的写清楚整个流程 string key = " ...