深入剖析javaScript中的深拷贝和浅拷贝

在面试时经常会碰到面试官问:什么是深拷贝和浅拷贝,请举例说明?如何区分深拷贝与浅拷贝,简单来说,假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,如果B没变,那就是深拷贝;我们先看两个简单的案例:

//案例1(深拷贝)
var a1 = 1, a2= a1;
console.log(a1) //1
console.log(a2) //1
a2 = 2; //修改 a2
console.log(a1) //1
console.log(a2) //2
//案例2(浅拷贝)
var o1 = {x: 1, y: 2}, o2 = o1;
console.log(o1) //{x: 1, y: 2}
console.log(o2) //{x: 1, y: 2}
o2.x = 2; //修改o2.x
console.log(o1) //{x: 2, y: 2}
console.log(o2) //{x: 2, y: 2}

按照常规思维,o1应该和a1一样,不会因为另外一个值的改变而改变,而这里的o1 却随着o2的改变而改变了。同样是变量,为什么表现不一样呢?为了更好的理解js的深浅拷贝,我们先来理解一些js基本的概念 —— 目前JavaScript有五种基本数据类型(也就是简单数据类型),它们分别是:Undefined,Null,Boolean,Number和String。还含有一种复杂的数据类型(也叫引用类型),就是对象,引用类型有:Object、Array、Function(之所以说“目前”,因为之后也可能会有新的类型出来。)

一、基本类型和引用类型

ECMAScript变量可能包含两种不同数据类型的值:基本类型值和引用类型值。基本类型值指的是那些保存在栈内存中的简单数据段,即这种值完全保存在内存中的一个位置。而引用类型值是指那些保存堆内存中的对象,意思是变量中保存的实际上只是一个指针,这个指针指向内存中的另一个位置,该位置保存对象。

打个比方,基本类型和引用类型在赋值上的区别可以按“双胞胎”和“影子”来理解:基本类型赋值等于一位妈妈生的双胞胎,二者互不相关,各自有各自的特性;而引用类型赋值相当于自己跟影子,你在变化时影子会随着变化;

上面清晰明了的介绍了基本类型和引用类型的定义和区别。

再回到前面的案例,案例1中的值为基本类型,案例2中的值为引用类型。案例2中的赋值就是典型的浅拷贝,并且深拷贝与浅拷贝的概念只存在于引用类型。

二、深拷贝与浅拷贝

既然已经知道了深拷贝与浅拷贝的来由,那么该如何实现深拷贝?我们先分别看看Array和Object自有方法是否支持:

1、Array

对于数组我们可以使用slice() 和 concat() 方法来解决上面的问题

1) slice

var arr1 = [‘a‘, ‘b‘], arr2 = arr1.slice();
console.log(arr1); // ["a", "b"]
console.log(arr2); // ["a", "b"]
arr2[0] = ‘c‘; //修改arr2
console.log(arr1); // ["a", "b"]
console.log(arr2); // ["c", "b"]

此时,arr2的修改并没有影响到arr1,看来深拷贝的实现并没有那么难嘛。

2) concat

var arr1 = [‘a‘, ‘b‘], arr2 = arr1. concat ();
console.log(arr1); // ["a", "b"]
console.log(arr2); // ["a", "b"]
arr2[0] = ‘c‘; //修改arr2
console.log(arr1); // ["a", "b"]
console.log(arr2); // ["c", "b"]

我们把arr1改成二维数组再来看看:

var arr1 = [‘a‘, ‘b‘, [‘c‘, ‘d‘]], arr2 = arr1.concat();
arr2[2][1] = 100;
console.log(arr1); //[‘a‘, ‘b‘, [‘c‘, 100]]
console.log(arr2); //[‘a‘, ‘b‘, [‘c‘, 100]]

咦,arr2又改变了arr1,看来slice()/concat()只能实现一维数组的深拷贝

除了上面两种方法外,我们还可以借用JQ的extend方法。

$.extend( [deep ], target, object1 [, objectN ] )

deep表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝

target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。

object1 objectN可选。 Object类型 第一个以及第N个被合并的对象。

var a=[0,1,[2,3],4],
b=$.extend(true,[],a);
a[0]=1;
a[2][0]=1; //
console.log(a); //[1,1,[1,3],4]
console.log(b); //[0,1,[2,3],4]

不过这种方法需要依赖JQ库。

2、Object

1) 利用对象的深拷贝实现原理

定义一个新的对象,遍历源对象的属性并赋给新对象的属性

var obj = {
  name:‘sonia‘,
  age: 18
}
var obj2 = new Object();
obj2.name = obj.name;
obj2.age = obj.age;
obj.name = ‘alice‘;
console.log(obj); //Object {name: "‘alice‘", age: 18}
console.log(obj2); //Object {name: "‘sonia‘", age: 18}

理解了以上的基本思想,我们就可以封装一个方法 deepCopy来实现对象的深拷贝,代码如下

var obj = {
  name: ‘sonia‘,
  age: 18
}
var deepCopy = function (source) {
  var result = {};
  for(var key in source) {
  if(typeof source[key] === ‘object‘) {
    result[key] = deepCopy(source[key])
  } else {
    result[key] = source[key]
  }
}
  return result;
}
var objCopy = deepCopy(obj)

obj.name = ‘aaa‘;

console.log(obj);//Object {name: "aaa", age: 18}

console.log(objCopy);//Object {name: "sonia", age: 18}

2) JSON.parse(JSON.stringify(obj))

var obj = {
  name: ‘sonia‘,
  age: 18
}
var obj2 = JSON.parse(JSON.stringify(obj));
obj.name = ‘alice‘;
console.log(obj) // {name: "alice", age: 18}
console.log(obj2) // {name: "sonia", age: 18}

了解拷贝也不仅仅是为了应付面试,在实际开发中也是非常有用的。

原文地址:https://www.cnblogs.com/ycg-myblog/p/9821275.html

时间: 2024-10-20 08:13:49

深入剖析javaScript中的深拷贝和浅拷贝的相关文章

Javascript中的深拷贝和浅拷贝

文章目录 JavaScript中的变量类型 深拷贝和浅拷贝的理解 深拷贝和浅拷贝的实现方式 为什么需要深拷贝和浅拷贝 JavaScript中的变量类型 (1).基本类型 JavaScript中的基本类型有五种: null.undefined.boolean.string.number. 变量是按值存放的,存放在栈中的简单数据段,可以直接访问. (2).引用类型 引用类型包括对象和数组,其存储在堆当中,而变量是指针,指向堆. 当我们访问的时候,实际上是访问指针,然后指针去寻找对象或数组. 深拷贝与

Java中的深拷贝和浅拷贝 原型模式

1: Java中浅拷贝和深拷贝的定义:      浅拷贝:就是指两个对象共同拥有同一个值,一个对象改变了该值,也会影响到另一个对象.      深拷贝:就是两个对象的值相等,但是互相独立. (深拷贝才是真正的拷贝,浅拷贝只是将引用指向了同一份对象) 2:Java中几种常见的拷贝操作: (1)"="操作:也就是赋值操作: (2)拷贝构造函数:拷贝构造函数就是构造函数的参数的类型是该构造函数所在的类,即参数就是该类的一个对象. <span style="font-size:

浅谈Java中的深拷贝和浅拷贝

浅谈Java中的深拷贝和浅拷贝(转载) 原文链接: http://blog.csdn.net/tounaobun/article/details/8491392 假如说你想复制一个简单变量.很简单: [java] view plaincopyprint? int apples = 5; int pears = apples; int apples = 5; int pears = apples; 不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short,float

**Python中的深拷贝和浅拷贝详解

Python中的深拷贝和浅拷贝详解 这篇文章主要介绍了Python中的深拷贝和浅拷贝详解,本文讲解了变量-对象-引用.可变对象-不可变对象.拷贝等内容. 要说清楚Python中的深浅拷贝,需要搞清楚下面一系列概念: 变量-引用-对象(可变对象,不可变对象)-切片-拷贝(浅拷贝,深拷贝) [变量-对象-引用] 在Python中一切都是对象,比如说:3, 3.14, 'Hello', [1,2,3,4],{'a':1}...... 甚至连type其本身都是对象,type对象 Python中变量与C/

python中的深拷贝和浅拷贝

1.深拷贝VS浅拷贝 python中的深拷贝和浅拷贝和java里面的概念是一样的, 所谓浅拷贝就是对引用的拷贝 (里面的数据不拷贝出来,其中的数据与原对象里面数据用的是相同的地址空间) 所谓深拷贝就是对对象的资源的拷贝 (里面的数据拷贝出来.深拷贝有自己的存储空间,有自己定义的数据,跟原对象一点关系也没有) 2.对赋值的认识: 赋值:将一个对象的地址赋值给一个变量,让变量指向该地址( 旧瓶装旧酒 ) 修改不可变对象(str.tuple)需要开辟新的空间 修改可变对象(list等)不需要开辟新的空

Javascript学习之深拷贝和浅拷贝详解

在JavaScript 中,存在着这样的两种拷贝方式.分别是:深拷贝和浅拷贝,这两种拷贝在实际中非常的常见,如果读者是一个阅读源码的爱好者,相信多多少少对深拷贝和浅拷贝有所了解.本文和大家分享的就是深拷贝和浅拷贝相关内容,一起来看看吧,希望对大家 学习javascript有所帮助. 一.浅拷贝 浅拷贝在现实中最常见的表现在赋值上面,例如 <!DOCTYPE html> <html lang="en"> <head> <meta charset=

Objective-C中的深拷贝和浅拷贝

在Objective-C中对象之间的拷贝分为浅拷贝和深拷贝.说白了,对非容器类的浅拷贝就是拷贝对象的地址,对象里面存的内容仍然是一份,没有新的内存被分配.对非容器类的深拷贝就是重写分配一块内存,然后把另一个对象的内容原封不动的给我拿过来.对容器类的深拷贝是对容器中的每个元素都进行拷贝,容器类的浅拷贝是对容器里的内容不进行拷贝,两个容器的地址是不同的,但容器里的所装的东西是一样的,在一个容器中修改值,则另一个浅拷贝的容器中的值也会变化.所以对非容器类看对象是否为深拷贝还是浅拷贝就得看对象的内存地址

C++中的深拷贝和浅拷贝 QT中的深拷贝,浅拷贝和隐式共享

下面是C++中定义的深,浅拷贝 当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用.也就是说,当类的对象需要拷贝时,拷贝构造函数将会被调用.以下情况都会调用拷贝构造函数: (1)一个对象以值传递的方式传入函数体 (2)一个对象以值传递的方式从函数返回 (3)一个对象需要通过另外一个对象进行初始化. 如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝.位拷贝又称浅拷贝,后面将进行说

iOS中的 深拷贝和浅拷贝

#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { // 一: copy 与 retain 的区别 // 谁才有引用计数的概念: // 1. 堆区空间才有引用计数概念. // 2. 堆区的对象才会有引用计数. //%ld: - 1 %lu:18446744073709551615 //retain:始终是浅拷贝.引用计数每次加一. //返回对象是否可变与被复制的对象保持一致. //copy:对于