深入学习javaScript中的深拷贝(复制)和浅拷贝(复制)

news/2024/7/19 13:28:14 标签: js, 浅拷贝和深拷贝

学习深拷贝和浅拷贝之前,首先我们需要知道什么是基本数据类型和引用数据类型,如果还不知道或不是特别清楚的话,请点击https://blog.csdn.net/lhjuejiang/article/details/79614614

嗯、步入正题

所谓的浅复制,只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用(仅仅是指向被复制的内存地址,如果原地址改变了,那么浅复制出来的对象也会相应的改变),我们把这种拷贝叫做“(浅复制)浅拷贝”。

而深复制的话,深复制在计算机中开辟了一块内存地址用于存放复制的对象,所以复制出来的对象和原对象之间不会相互影响。

深复制和浅复制最根本的区别在于是否是真正获取了一个对象的复制实体,而不是引用。

下面我们先来看看浅拷贝的例子

首先看的是基本类型的复制

var a=123;
var b=a;
a=123456;
console.log(a); //123456
console.log(b); //123

//或者是

var a='afafas';
var b=a;
a='fgfdsdsgs';
console.log(a); //fgfdsdsgs
console.log(b); //afafas
像这种基本数据类型,我们进行简单的复制,不会发生引用

然后再来看看引用类型(对象)的浅拷贝

我们知道对象的话,我们直接赋值(浅拷贝)是会发生相互引用的,原对象和新对象之间不管谁改变都会影响到另一个,因为这种复制,只是将复制出来的东西的指向指向了要复制的那个东西的内存地址,简单的说,就是两个都同时指向了一个空间,如果改变其中一个,另一个也会发生变化。这就发生了引用。

看例子呗

var person={name:'leo',age:18};

var son={sex:'男'};

function clone(p,s){

  var s=s||{};//判断s对象是否存在,不存在则定义s为空对象

  for(var prop in p){

    s[prop]=p[prop];
};
    return s;//返回s对象

};

clone(person,son);

调用clone函数最终返回的结果为 {sex: "男", name: "leo", age: 18}.

但是假如 var person={name:'leo',age:18,man:{hight:188}};即person 对象里包含一个子对象man;

那么在复制时浅拷贝只是将子对象的一个引用地址复制给son对象;son和person里的man 属性都指向同一个对象,故改变任一个都能影响到另外一个:例子如下:
 

var person={name:'chen',age:18,man:{hight:188}}

var son={sex:'男'};


function clone(p,s){

  var s=s||{};//判断s对象是否存在,不存在则定义s为空对象

  for(var prop in p){

    s[prop]=p[prop];

};

    return s;//返回s对象 

};

clone(person,son);

son.name='ze';

console.log(son.name);//ze

console.log(person.name);//leo

son.man.hight=200;

console.log(son.man.hight);//200

console.log(person.man.hight);//200

2、

Object.assign()是ES6新增函数,Object.assign()方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。(可以进行一层的深度拷贝,后面会讲到)

Object.assign(target, ...sources)

参数:

target:目标对象。
sources:任意多个源对象。
返回值:目标对象会被返回。

var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);

initalObj.a.a = "changed";
console.log(obj.a.a); // "changed"

3、jQuery中的$.extend()默认也是浅拷贝

var person={name:'leo',age:18}

var son={sex:'男'};

$.extend(son,person);
console.log(son);//{sex: "男", name: "leo", age: 18}

想要解决两个对象之间的相互引用问题,就需要用到深拷贝喽,因为我们前边已经说过了深复制在计算机中开辟了一块内存地址用于存放复制的对象,所以复制出来的对象和原对象之间不会相互影响。
那么深拷贝(深复制)到底怎么实现的呢?办法有很多了。针对上面的这个问题

1、采用递归的方法去拷贝要复制的对象,看例子咯

var person={name:'leo',age:18,man:{hight:188}}

var son={sex:'男'};

function clone(p,s){
  var s=s||{};//判断s对象是否存在,不存在则定义s为空对象

  for(var prop in p){

    if(typeof p[prop]=='object'){

        s[prop]=(p[prop].constructor===Array)?[]:{};//三元运算,将s[prop]初始化为数组或者对象

        clone(p[prop],s[prop])

    }

    else{

      s[prop]=p[prop];
    }
    
};

    return s;//返回s对象

};


clone(person,son);

son.name='ze';

console.log(son.name);//ze

console.log(person.name);//leo
son.man.hight=200;

console.log(son.man.hight);//200
console.log(person.man.hight);//188
2、利用jQuery中的$.extend()方法喽
$.extend(),默认是浅拷贝,但如果我们将第一个参数设为true,即为深拷贝,即$.extend(true,b,a)的形式

嗯、看例子吧

var person={name:'leo',age:18,man:{hight:188}}

var son={sex:'男'};
$.extend(true,son,person);
son.name='ze';

console.log(son.name);//ze

console.log(person.name);//leo

son.man.hight=200;

console.log(son.man.hight);//200

console.log(person.man.hight);//188

当然,还有别的方法,但目前鄙人还没深入研究,所以就不咋这里误人子弟,以后有时间也会做相应的研究再做更新

2、对象只有一层的话可以使用:函数

Object.assign({},obj1)的意思是先建立一个空对象{},接着把obj1中所有的属性复制过去,所以obj2会长得跟obj1一样,这时候再修改obj2.b也不会影响obj1。


因为Object.assign跟我们手动复制的效果相同,所以一样只能处理深度只有一层的对象,没办法做到真正的 深拷贝。不过如果要复制的对象只有一层的话可以考虑使用它。如下

var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = Object.assign({}, obj1);
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(obj2);
// { a: 10, b: 100, c: 30 }
JSON对象中的parse和stringify,JOSN对象中的stringify可以把一个js对象序列化为一个JSON字符串,parse可以把JSON字符串反序列化为一个js对象,通过这两个方法,也可以实现对象的深复制。
var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 10 } } <-- 沒被改到
console.log(obj2);
// { body: { a: 20 } }
console.log(obj1 === obj2);
// false
console.log(obj1.body === obj2.body);
// false

这样做是真正的深拷贝这种方法简单易用。

但是这种方法也有不少坏处,譬如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。

这种方法能正确处理的对象只有Number,String,Boolean,Array,扁平对象、即那些能够被 json 直接表示的数据结构。RegExp对象是无法通过这种方式深拷贝。

也就是说,只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON。

var obj1 = { fun: function(){ console.log(123) } };
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(typeof obj1.fun);
// 'function'
console.log(typeof obj2.fun);
// 'undefined' <-- 没复制

要复制的function会直接消失,所以这个方法只能用在单纯只有数据的对象。

那么对于数组,ES6我们复制有新的两种方法,不会发生引用。

第一种:Array.from(要复制的数组);

var arr1=[1,2,3];
var arr2=Array.from(arr1);
arr1.push(4);
alert(arr1);  //1234
alert(arr2);  //123
arr2.push(5);
alert(arr1);  //1234
alert(arr2);  //1235

第二种:...

var arr1=[1,2,3];
var arr2=[...arr1];
arr1.push(4);
alert(arr1);  //1234
alert(arr2);  //123
arr2.push(5);
alert(arr1);  //1234
alert(arr2);  //1235

第二种这个方法也可以用在函数的行参上面。

function show(...arr1){  //直接来复制arguments这个伪数组,让它变成真正的数组,从而拥有数组的方法。
  alert(arr1); //1234
  arr1.push(5);
  alert(arr1); //12345
}
show(1,2,3,4)

或者是通过循环来复制:

var arr1=[1,2,3,4];
var arr2=[];
for(var i=0; i<arr1.length; i++){
  arr2[i]=arr1[i];
}
arr1.push(5);
arr2.push(6);
alert(arr1); //12345
alert(arr2); //12346

还有什么疑问,或者想要了解更多的内容的请点击下面的推荐文章,很不错的哦

推荐:

http://www.dengzhr.com/js/1180

https://mp.weixin.qq.com/s/FFp56h9MnMKK4OM0BjDd1Q

http://jerryzou.com/posts/dive-into-deep-clone-in-javascript/










http://www.niftyadmin.cn/n/952021.html

相关文章

泛型学习(二)

开放泛型类型: 未提供类型参数的泛型类型封闭构造泛型类型: 为所有类型参数提供参数 转载于:https://www.cnblogs.com/zhangzheny/archive/2007/12/22/1010111.html

什么是强类型,强类型集合

为所有变量指定除object的数据类型就称为“强类型”...为集合类成员指定除object的数据类型就称为“强类型集合”... 转载于:https://www.cnblogs.com/zhangzheny/archive/2007/12/24/1012071.html

帝豪gs车机系统wince_品质的选择帝豪GS,带你去感受它的魅力

对于吉利帝豪GS&#xff0c;我那是由内及外的喜欢&#xff0c;所以我才会选择它。外观是所有消费者显而易见的&#xff0c;而对于内饰来说&#xff0c;这个&#xff0c;就只有车主自己的感受才是最深的。我选择吉利帝豪GS&#xff0c;只是觉得它适合我。当初买车时经济的考虑&a…

浏览器渲染页面流程

下面是渲染引擎在取得内容之后的基本流程&#xff1a;解析HTML以构建dom树-构建render树-布局render树-绘制render树下面是浏览器渲染页面的具体过程&#xff1a;1.浏览器解析html源码&#xff0c;然后创建一个 DOM树。&#xff08;所谓dom树指&#xff1a;浏览器将HTML解析成树…

多态的实现方法有几种_Java入门:基础知识(面向对象:多态)

接着上一篇的基础知识&#xff0c;今天俺们来学习一下&#xff1a;面向对象(万物皆对象)三大特征(封装、继承、多态)对象&#xff1a;多态(面向对象三大特征之一)多态 概述&#xff1a; 多态是继封装、继承之后&#xff0c;面向对象的第三大特性。 指的是同一…

响应式和自适应布局的区别

自适应&#xff1a;自己根据屏幕宽度的改变而改变&#xff08;典型的写法不需要media判断&#xff0c;直接让每个元素通过相对的宽度&#xff0c;比如百分比、vh、em 、rem等来改变容器的大小&#xff0c;文字的大小&#xff09; 响应式&#xff1a;根据设备的不同而展示不同的…

多个绝对值相加求最大值问题_动点问题很难?真正难倒大家并不是题目的容量...

中考作为一项全国性的选拔人才考试&#xff0c;不仅会考查考生知识定理的掌握熟悉程度&#xff0c;还会考查大家分析问题和解决问题的能力&#xff0c;而且考生对数学思想方法的理解和认知也是中考数学重点考查对象。说到数学思想方法&#xff0c;想必很多人已经非常熟悉了&…

c#接口和抽象类的区别

大家都容易把这两者搞混,我也一样,在听李建忠老师的设计模式时,他也老把抽象类说成接口,弄的我就更糊涂了,所以找了些网上的资料.一、抽象类&#xff1a;抽象类是特殊的类&#xff0c;只是不能被实例化&#xff1b;除此以外&#xff0c;具有类的其他特性&#xff1b;重要的是抽…