深拷贝和浅拷贝的区别

  浅拷贝(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. 【Java每日一题】20170217

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

  2. Java基础IO流(五)RandomAccessFile

    RandomAccessFile java提供的对文件内容的访问,既可以读文件也可以写文件.RandomAccessFile支持随机访问文件,可以访问文件的任意位置 (1)java文件模型:    在 ...

  3. jQuery: 选择器,筛选器

    jQuery 简介 jQuery由美国人John Resig创建,至今已吸引了来自世界各地的众多 javascript高手加入其team. jQuery是继prototype之后又一个优秀的Javas ...

  4. Spider-two

    一.网络数据加密:1. md5 / sha1 不可逆加密算法: 结果是十六进制数, 结果不可逆, 多用于文件验证 import hashlib md5_obj = hashlib.md5() sha1 ...

  5. font-face在ie无法识别问题

    font-face在ie的时候,需要其他格式eot,但是按照网上的设置无法识别,需要把原来的fotmat设置成format('eot');

  6. 【效率工具】SSH一键登录脚本(可一键从跳板机登录线上服务器)

    说明 前阵子上线,一次性上了十个服务,一直上到凌晨才完事,期间每个服务都要先输入跳板机的登录信息来登录跳板机,然后再输入线上服务器的信息来登录线上服务器,实在是太过于麻烦,而且有些服务还有好几台服务器 ...

  7. 章节四、4-For循环

    一.For循环格式 package introduction5; public class ForLoopDemo { public static void main(String[] args) { ...

  8. jQuery实现画面的展开、收起和停止

    主要用到动画效果中的三个操作 ("#id").slideDown(3000): // 后面的数字表示效果的时长 ("#id").stop(); ("# ...

  9. shell编程-语句(八)

    if else判断语句 if语句 语法格式1: if 条件 then 语句1 语句2 ... 语句n fi 语法格式2: 常用于终端命令提示符中 if [条件]:then 语句:fi if else语 ...

  10. 6. svg学习笔记-路径

    路径相比于多边形<polygon>元素具有更强绘图能力,<polygon>元素可以绘制任意的多边形,而路径可以绘制任意的轮廓线,是线段,曲线,圆弧的组合形式.svg中可以使用& ...