JavaScript中对象的浅复制和深复制

在JavaScript中,如果要复制一个变量我们应该怎么做呢?下面这种做法是最简单的一种形式:

//把a复制给b
let a = 12;
let b = a;

这种复制方法只能适用于基本类型,如果a是对象怎么办呢?我们先来看看上面的代码在内存中做了什么事:

声明了变量a = 12,栈内存会分配一块区域来存储,如上图所示。把a赋给b,会在栈中重新开辟一块区域来存储b,并且b的值就是a的值。

假如a是对象,内存做了什么事呢?来看下面的例子:

let a = {};
let b = a;


如图所示,对象是存储在堆内存中的,栈中保存的是地址值,使用这种方式复制对象只不过是复制了该对象的引用而已,对象实体还是只有一个。那么对象应该怎样复制呢?对象的复制其实也就是产生一个一模一样的对象,对象包含属性和方法,我们创建一个新对象,将旧对象的属性和方法赋给新对象,这样不就是复制了一个对象吗?顺着这个思路,我们来看下面的代码:

function copy(obj){
  //基本类型和函数直接返回
  if(!(obj instanceof Object) || typeof obj === ‘function‘) return obj;

  let newObj = {};
  if(obj instanceof Array) newObj = [];

  for(let p in obj){
    newObj[p] = obj[p];
  }
  return newObj;
}

let p = {
  name: ‘bob‘,
  friends: [‘jack‘, ‘rose‘]
}
let p2 = copy(p);
console.log(p2);

定义一个copy函数,接收一个参数,用以实现对象的复制,如果参数是基本类型或函数就直接返回。函数体内声明一个新对象newObj,然后遍历参数obj,将其每一个属性都赋给newObj,最后返回newObj。接着使用copy方法生成了p的一个复制对象p2,结果如下图所示:

但是这样有一个问题,我们来看下面的代码:

p2.friends.push(‘lily‘);
console.log(p2.friends);//["jack", "rose", "lily"]
console.log(p.friends);//["jack", "rose", "lily"]

我们给p2的friends添加了一个lily,结果致使p的friends也被同步修改了。下面的内存图可以帮助读者理解:

从图中可以看出,虽然p和p2分别指向了两个对象,但是里面的friends属性还是指向的同一个数组,问题在于这段代码:

for(let p in obj){
  newObj[p] = obj[p];
}

只进行了对象第一层的复制,对于对象里面引用类型的属性,则进行了地址值的复制,这就是所谓的浅复制,也就是说p.friends和p2.friends是同一个对象:

console.log(p.friends == p2.friends);//true

那如何进行彻底的复制——深复制呢?思路也很简单,在遍历赋值对象属性的时候,遇到属性是引用类型的,也需要把这个属性展开赋值一下,于是我们可以用递归的思想来实现,如下代码所示:

//深复制
function deepCopy(obj){
  //基本类型和函数直接返回
  if(!(obj instanceof Object) || typeof obj === ‘function‘) return obj;

  let newObj = {};
  if(obj instanceof Array) newObj = [];

  for(let p in obj){
    //继续复制对象里面的对象
    newObj[p] = deepCopy(obj[p]);
  }
  return newObj;
}

let p = {
  name: ‘bob‘,
  friends: [‘jack‘, ‘rose‘]
}
let p2 = deepCopy(p);

p2.friends.push(‘lily‘);
console.log(p2.friends);//["jack", "rose", "lily"]
console.log(p.friends);//["jack", "rose"]
console.log(p.friends == p2.friends);//false

声明了deepCopy函数,用于实现深复制,函数体和浅复制的函数体基本相同,只是在遍历要复制的对象的时候添加了一个判断,如果属性是基本类型或函数则进行赋值操作,否则递归调用deepCopy,继续复制对象里面的对象。接着演示了deepCopy的使用,发现修改了p2.friends并不会影响p.friends,它们已经是两个对象了。

对于浅复制,ES6中提供了Object.assign()方法,如下代码所示:

let p3 = Object.assign({}, p);
console.log(p3.friends == p.friends);//true

不知读者是否还记得,在JavaScript继承(四)——原型式继承中提到过Object.create()方法,从使用效果上来看,Object.create()创建的新对象有点类似浅复制,只不过新对象和原对象是一种继承关系,而Object.assign()创建的新对象和原对象是彼此独立的,如下代码所示:

let p4 = Object.create(p);
console.log(p4.__proto__ === p);//true
console.log(p3.__proto__ === p);//false

原文地址:https://blog.51cto.com/13981400/2359244

时间: 2024-11-17 00:01:58

JavaScript中对象的浅复制和深复制的相关文章

深度解析javascript中的浅复制和深复制

原文:深度解析javascript中的浅复制和深复制 在谈javascript的浅复制和深复制之前,我们有必要在来讨论下js的数据类型.我们都知道有Number,Boolean,String,Null,Undefined,Object五种类型.而Object又包含Function,Array和Object自身.前面的五种类型叫做基本类型,而Object是引用类型.可能有人就要问,为什么要分基本类型和引用类型呢?后面你就会明白的. 我们首先来看看浅复制和深复制的简洁定义: 深复制:直接将数据复制给

转载:python中的copy模块(浅复制和深复制)

主要是介绍python中的copy模块. copy模块包括创建复合对象(包括列表.元组.字典和用户定义对象的实例)的深浅复制的函数. ########copy(x)########创建新的复合对象并通过引用复制x的成员来创建x的浅复制.更加深层次说,它复制了对象,但对于对象中的元素,依然使用引用.对于内置类型,此函数并不经常使用.而是使用诸如list(x), dict(x), set(x)等调用方式来创建x的浅复制,要知道像这样直接使用类型名显然比使用copy()快很多.但是它们达到的效果是一样

c++中浅复制与深复制

在C++中经常会遇到有关类对象的浅复制与深复制的问题,也是容易出错的地方. 查找了相关资料,有关浅复制与深复制的定义为:对类进行复制的时候按位复制,即把一个对象各数据成员的值原样复制到目标对象中.当类中涉及到指针类型数据成员的时候,往往就会产生指针悬挂问题. class A{ public: int* a; }; A a1; A b1=a1; b1=a1执行的是浅复制,此时a1.a和b1.a指向的是同一个内存地址,如果在析构函数里面有对内存的释放.就会出现内存访问异常.因为一块内存空间会被释放两

(知其所以然 主题2)从底层分析OC中ARC和非ARC下深复制和浅复制

今天,在坊间听到有人在争论OC中关于NSString的深浅复制,听了下,感觉很有必要来一个分析总结,让我们从底层去了解OC中深浅复制的运作机制. 所谓copy就是在原有对象的基础上产生一个副本对象,遵循最关键的两点原则: 1. 改变原对象的属性和行为不会对副本对象产生任何影响 2. 改变副本对象的属性和行为不会对原对象产生任何影响 在理解了这一层之后,我们一起来研究下deep copy 和 shallow copy,因为苹果是一个非常注重性能的公司,所以拷贝在底层实现没那么简单: 以NSStri

原型模式——浅复制与深复制

原型模式涉及一个浅复制和深复制的概念.原型模式可以简单理解为“复制”,但这个复制不是代码的复制.对同一个类,我们可以实例化new三次来“复制”,但如果在初始化的时候构造函数的执行很长,多次实例化就显得效率很低效了.那我们能否只实例化一次,然后“复制”呢? Test test1 = new Test(); Test test2 = test1; Test test3 = test1; 这样写吗?注意这是引用的复制,这实际上还是只有test1一个实例,test2.test3只是复制了其引用而已,如果

也来谈一谈js的浅复制和深复制

1.浅复制VS深复制 本文中的复制也可以称为拷贝,在本文中认为复制和拷贝是相同的意思.另外,本文只讨论js中复杂数据类型的复制问题(Object,Array等),不讨论基本数据类型(null,undefined,string,number和boolean),这些类型的值本身就存储在栈内存中(string类型的实际值还是存储在堆内存中的,但是js把string当做基本类型来处理 ),不存在引用值的情况. 浅复制和深复制都可以实现在已有对象的基础上再生一份的作用,但是对象的实例是存储在堆内存中然后通

JAVA浅复制与深复制

1.浅复制与深复制概念 ⑴浅复制(浅克隆)     多个变量指向一个对象    被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象. ⑵深复制(深克隆)     每个变量指向一个对象,同时对象内包含对象,能复制内部对象    被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量.那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象.换言之,深复制把要复

java基础-浅复制与深复制的理解

浅复制与深复制在很多编程语言中都有出现,那么什么是浅复制,什么是深复制呢? 要区分浅复制与深复制,首先我们要明确什么是复制,怎样才算是复制.复制的例子在生活中也随处可见,如复印一份文档,复制一段文字等.我们可以发现,复制操作后可以得到两份相同的东西,即复制由一变为二了.下面来看一个例子: public class User{ private int age; public int getAge(){ return age; } } User user1 = new User(); User us

iOS 浅赋值、深复制、完全复制的知识点梳理验证(附加归档解档)

写于前: 在之前转载的一片文章中,文中对浅复制和深复制进行了详细的解读,同时还提到了深复制(one-level-deep copy).完全复制(true copy)的概念,并指出iOS开发中的深复制是单层深赋值,本文将对这几个概念进行验证梳理. (单层和完全概念区分:例如多层数组只实现一层内容拷贝,其他层为指针拷贝成为单层深复制:若多层内容都实现拷贝称为完全赋值) 程序中用到的几点概念补充 (1) 浅复制(shallow copy):在浅复制操作时,对于被复制对象的每一层都是指针复制. 深复制(