深拷贝和浅拷贝的区别

  浅拷贝(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. Discuz网警过滤关键词库

    积累近几年discuz关键词过滤 使用方法:1.进入后台/内容/词语过滤批量添加.2.打开CensorWords.txt,复制里面的文本信息到批量添加的输入框内,点击确定即可.如图: 关键词下载:Ke ...

  2. Java学习笔记之——Object类

    所有类的祖先 如果一个类没有显式继承,则继承Object 每一个类都直接或间接的是Object的子类 相关API: protected Objectclone() 创建并返回此对象的副本. boole ...

  3. Advanced redirection features

    here are three types of I/O, which each have their own identifier, called a file descriptor: standar ...

  4. K8S Calico

    NetworkPolicy是kubernetes对pod的隔离手段,是宿主机上的一系列iptables规则. Egress 表示出站流量,就是pod作为客户端访问外部服务,pod地址作为源地址.策略可 ...

  5. JS与CSS阻止元素被选中及清除选中的方法总结

    有时候,我们希望阻止用户选中我们指定区域的文字或内容. 举个栗子,有时候用户在一个区域执行频繁的点击操作,一不小心傲娇地点多了,就会选中当前区域的内容. 再举个栗子,制作轮播组件的时候,点击下一页,若 ...

  6. Linux 安装 lrzsz,使用 rz、sz 上传下载文件

    yum install -y lrzsz 上传文件到服务器 rz 命令后会出现一个文件选择框,选择.确定即可 从服务器下载文件 sz 文件名即可

  7. robot framework笔记(一):环境配置(基于python3)+在pycharm中编写及运行robot脚本

    (一)使用pip安装robotframework框架和seleniumlibrary库 pip install --upgrade robotframework pip install --upgra ...

  8. TypeScript的概要和简介

    安装 Typescript的基本介绍可以自行百度 centos虚拟机中可以完整的体验, virtualbox安装开发版本,选择开发工具项,否则增强功能无法安装[提示kernel 头文件缺失,yum安装 ...

  9. MySQL 基本语句(1)

    一.cmd命令行的常用命令: 当我们使用MySQL 5.5 Command Line Client这个客户端登陆时,只能登陆root用户.如果今后创建了别的用户,就很麻烦了,所以我们不用MySQL 5 ...

  10. [20180918]文件格式与sql_id.txt

    [20180918]文件格式与sql_id.txt --//记录测试中遇到的一个问题.这是我在探究SQL*Net more data from client遇到的问题.--//就是实际oracle会把 ...