深拷贝和浅拷贝的区别

  浅拷贝(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. SpringBoot中并发定时任务的实现、动态定时任务的实现(看这一篇就够了)

    原创不易,如需转载,请注明出处https://www.cnblogs.com/baixianlong/p/10659045.html,否则将追究法律责任!!! 一.在JAVA开发领域,目前可以通过以下 ...

  2. 面试官:你分析过SpringMVC的源码吗?

    1. MVC使用 在研究源码之前,先来回顾以下springmvc 是如何配置的,这将能使我们更容易理解源码. 1.1 web.xml <servlet> <servlet-name& ...

  3. 向Spring容器中注册组件的方法汇总小结

    1.通过xml定义 <bean class=""> <property name="" value=""></ ...

  4. Vue项目build打包部署到Tomcat后,刷新报404错误解决方案

    问题描述: 一.更新依赖,并打包项目 cd /root/.jenkins/workspace/v-test;npm installcd /root/.jenkins/workspace/v-test; ...

  5. Javascript 跨域知识详细介绍

    JS跨域知识总结: 在“跨域”一词经常性地出现以前,我们其实已经频繁地使用它了.如在A网站的img,src指向B网站的某一图片地址,毫无疑问,这在通常情况下都是能正常显示的(且不论防盗链技术):同样, ...

  6. elementUI vue 编辑中的input的验证残留清除

    当使用编辑的时候, 假如上次的验证没通过, 报红了, 下次再点击编辑的时候还会报红,因此要清除验证残留, 方式有两种: this.$refs["from"].resetFields ...

  7. 2018-08-11 中文代码示例之Spring Boot 2.0.3问好

    上次试用Spring Boot还是两年前: 中文代码示例之Spring Boot 1.3.3演示. 打算用在一个讨论组内小项目上, 于是从官网Building an Application with ...

  8. NoHttp封装--07 自定义异步任务框架

    MainActivity: public class MainActivity extends Activity implements View.OnClickListener { .... @Ove ...

  9. <自动化测试方案_3>第三章、怎么样实现自动化测试?(How)

    第三章.怎么样实现自动化测试?(How) 自动化测试分为:代码单元自动化测试.API接口自动化测试.UI自动化测试 代码单元自动化测试,一般是无法做到的,因为项目的原因,代码单元是不做自动化,其测试是 ...

  10. DAY14(PYTHONS)生成器进阶

    def average(): sum = 0 count = 0 avg = 0 while True: #循环 num = yield avg #遇到yield就停止,防止一开始除数为0导致崩溃 s ...