-
2021-03-11 12:41:52
一,浅拷贝
①对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据。
②对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
二,深拷贝
设想一下,一个类有一个对象,其成员变量中又有一个对象,该对象指向另一个对象,另一个对象又指向另一个对象,直到一个确定的实例。这就形成了对象图。那么,对于深拷贝来说,不仅要复制对象的所有基本数据类型的成员变量值,还要为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象图进行拷贝!
三,总结
深拷贝对引用数据类型的成员变量的对象图中所有的对象都开辟了内存空间;而浅拷贝只是传递地址指向,新的对象并没有对引用数据类型创建内存空间。
四,深浅拷贝实现方法
public class Person implementsCloneable {privateString name;private intage;public Person(intage, String name) {this.age =age;this.name =name;
}publicPerson() {
}public intgetAge() {returnage;
}publicString getName() {returnname;
}//clone是浅拷贝//@Override//protected Object clone() throws CloneNotSupportedException {//return super.clone();//}//深拷贝
@Overrideprotected Object clone() throwsCloneNotSupportedException {
String name= new String(this.name);return new Person(23, name);
}public static void main(String[] args) throwsCloneNotSupportedException {
Person person1= new Person(23, "李四");//Person person2 = (Person) person1.clone();//浅拷贝
Person person2 = (Person) person1.clone();//深拷贝
String result = person1.getName() == person2.getName() ? "clone是浅拷贝" : "clone是深拷贝";
System.out.println(result);
}
}
更多相关内容 -
JS赋值、浅拷贝和深拷贝(数组和对象的深浅拷贝)实例详解
2020-11-20 21:19:02本文实例讲述了JS赋值、浅拷贝和深拷贝(数组和对象的深浅拷贝)。分享给大家供大家参考,具体如下: 深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。 浅拷贝 只是拷贝了基本类型的数据,而引用类型... -
JavaScript基础心法 深浅拷贝(浅拷贝和深拷贝)
2020-12-09 09:46:23说到深浅拷贝,必须先提到的是JavaScript的数据类型,之前的一篇文章JavaScript基础心法——数据类型说的很清楚了,这里就不多说了。 需要知道的就是一点:JavaScript的数据类型分为基本数据类型和引用数据类型。 ... -
1.深浅拷贝.pptx
2021-09-22 17:19:23深浅拷贝ppt -
c++中深浅拷贝以及写时拷贝的实现示例代码
2020-12-31 02:34:44本文主要给大家介绍了关于c++中深浅拷贝及写时拷贝实现的相关内容,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍: 一:浅拷贝&深拷贝 浅拷贝:在拷贝构造的时候,直接将原内容的地址交给要拷贝的类... -
Python字典深浅拷贝与循环方式方法详解
2020-12-20 17:39:34深浅拷贝 循环方式 字典常用方法总结 一、深浅拷贝 列表、元组、字典(以及其他) 对于列表、元组和字典而言,进行赋值(=)、浅拷贝(copy)、深拷贝(deepcopy)而言,其内存地址是变化不通的。 赋值(=) 赋值只是创建... -
js中的深浅拷贝问题简析
2020-10-17 00:58:48主要给大家介绍了关于js中的深浅拷贝问题的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用js具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧 -
C#深浅拷贝的深入解析
2020-08-25 11:27:38主要给大家介绍了关于C#深浅拷贝的深入解析,文中通过示例代码介绍的非常详细,对大家的学习或者使用C#具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧 -
Android下Activity间通信序列化过程中的深浅拷贝浅析
2020-08-29 03:03:49主要给大家介绍了关于Android下Activity间通信序列化过程中深浅拷贝的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。 -
浅述python中深浅拷贝原理
2020-12-25 11:33:05前言 在c++中参数传递有两种形式:值传递和引用传递。这两种方式的区别我不在此说,自行补上,如果你不知道的话。我先上python代码,看完我们总结一下,代码如下: # copy module import import copy ... -
javascript简单实现深浅拷贝过程详解
2020-10-16 06:36:25主要介绍了javascript简单实现深浅拷贝过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 -
详解深浅拷贝
2021-07-12 18:03:28说道拷贝大家可能联想到深拷贝和浅拷贝概念,然而在swift却很少涉及宝贝问题,即使swift下依然有copy和mutableCopy方法。但终其原因,创建单纯的swift类并不需要继承NSObject,而是使用swift类,另外很多牵扯拷贝问题...前言
说道拷贝大家可能联想到深拷贝和浅拷贝概念,然而在swift却很少涉及宝贝问题,即使swift下依然有copy和mutableCopy方法。但终其原因,创建单纯的swift类并不需要继承NSObject,而是使用swift类,另外很多牵扯拷贝问题的数组和字典,在swift对应于Array和Dictionary,不同于NSArray 和NSDictionary,swift的Array和Dictionary是值类型,赋值后并不需要担心其拷贝问题。
所以主要Objective-C中的深拷贝和浅拷贝。
什么是深拷贝?什么是浅拷贝???
深拷贝是深度拷贝,是拷贝一个实例对象到一个新的内存地址,
浅拷贝只是简单拷贝一个实例对象的指针。
官方文档提供了这样一个图
从图中可以看到,集合的
浅拷贝后的数组Array2和Array1 指向同一段内存区域而深拷贝Array2和Array1 分别指向不同的内存区域
从这一点来看我们刚刚所说的是正确的,
非集合拷贝
不可变字符串(NSString)
代码
NSString *string = @"123"; NSString *stringCopy = [string copy]; NSMutableString *stringMutable = [string mutableCopy]; NSLog(@"string addrerss:%p",string); NSLog(@"stringCopy addrerss:%p",stringCopy); NSLog(@"stringMutable addrerss:%p",stringMutable);
打印结果
- NSString 进行copy 只是对其指针拷贝
- NSString 进行 mutableCopy 是真正创建一份新的对象
不可变的字符串是存在内存常量,因此可以看到两处地址相差甚远
1.NSString 是不可变的,对其copy也仍然是相同的内容,因此copy后返回自身,所以仍然是相同的地址
2.mutableCopy 表明或许真的需要一份新的可能,因此返回一个新的对象可变字符串(NSMutableString)
NSMutableString *mString = [[NSMutableString alloc]initWithString:@"123"]; NSString *mStringCopy = [mString copy]; NSString *mStringMutable = [mString mutableCopy]; NSLog(@"mString addrerss:%p",mString); NSLog(@"mStringCopy addrerss:%p",mStringCopy); NSLog(@"mStringMutable addrerss:%p",mStringMutable); NSLog(@"mStringCopy is mutable ? %@",[mStringCopy isKindOfClass:[NSMutableString class]] ? @"YES" : @"NO"); NSLog(@"mStringMutable is mutable ? %@",[mStringMutable isKindOfClass:[NSMutableString class]] ? @"YES" : @"NO");
- NSMutableString 的copy 变成是一个不可变的字符串
- mutableCopy 返回的是一个可变的字符串
- NSMutableString 无论copy还是mutableCopy 地址都不一样
总结
1.自定义的类需要实现深拷贝和浅拷贝,只要实现NSCoping协议即可。
2.如果实现浅拷贝,只需要在copyWithZone:方法返回自身即可
3.深拷贝:取决于每一层对象的本身,这个需要完全拷贝。则每一层对象都应该实现copyWithZone:中创建好新的对象,如果每一层都做到了深拷贝,那么最外层就是完全拷贝。集合类对象拷贝(copy/mutableCopy)
集合类不可变对象
NSString *str1 = @"hello world"; NSMutableString *str2 = [NSMutableString stringWithString:@"hello world"]; NSArray *array1 = [NSArray arrayWithObjects: str1, str2, nil]; NSArray *array2 = [array1 copy]; NSArray *array3 = [array1 mutableCopy]; NSLog(@"\n array1 = %p class = %@ \n", array1, [array1 class]); NSLog(@"\n array2 = %p class = %@ \n", array2, [array2 class]); NSLog(@"\n array3 = %p class = %@ \n", array3, [array3 class]); NSLog(@"\n\n======== 元素是String ======== "); NSLog(@"\n obj0 = %p class = %@ \n", array1[0], [array1[0] class]); NSLog(@"\n obj0 = %p class = %@ \n", array2[0], [array2[0] class]); NSLog(@"\n obj0 = %p class = %@ \n", array3[0], [array3[0] class]); NSLog(@"\n\n======== 元素是mutableString ========"); NSLog(@"\n obj1 = %p class = %@ \n", array1[1], [array1[1] class]); NSLog(@"\n obj1 = %p class = %@ \n", array2[1], [array2[1] class]); NSLog(@"\n obj1 = %p class = %@ \n", array3[1], [array3[1] class]);
输出
2021-05-08 17:34:32.740896+0800 AlgorithmDemo[39309:14557776] array1 = 0x102904180 class = __NSArrayI array2 = 0x102904180 class = __NSArrayI array3 = 0x102904260 class = __NSArrayM ======== 元素是String ======== obj0 = 0x100008220 class = __NSCFConstantString obj0 = 0x100008220 class = __NSCFConstantString obj0 = 0x100008220 class = __NSCFConstantString ======== 元素是mutableString ======== obj1 = 0x1028719b0 class = __NSCFString obj1 = 0x1028719b0 class = __NSCFString obj1 = 0x1028719b0 class = __NSCFString
由打印可得:
- 集合类不可变对象的copy为浅拷贝
- mutableCopy为深拷贝
- 无论copy还是mutableCopy集合内元素都是浅拷贝
集合类可变对象
NSString *str1 = @"hello world"; NSMutableString *str2 = [NSMutableString stringWithString:@"hello world"]; NSMutableArray *array1 = [NSMutableArray arrayWithObjects: str1, str2, nil]; NSMutableArray *array2 = [array1 copy]; NSMutableArray *array3 = [array1 mutableCopy]; NSLog(@"\n array1 = %p class = %@ \n", array1, [array1 class]); NSLog(@"\n array2 = %p class = %@ \n", array2, [array2 class]); NSLog(@"\n array3 = %p class = %@ \n", array3, [array3 class]); NSLog(@"\n\n======== 元素是mutableString ========"); NSLog(@"\n obj0 = %p class = %@ \n", array1[0], [array1[0] class]); NSLog(@"\n obj0 = %p class = %@ \n", array2[0], [array2[0] class]); NSLog(@"\n obj0 = %p class = %@ \n", array3[0], [array3[0] class]); NSLog(@"\n\n======== 元素是String ======== "); NSLog(@"\n obj1 = %p class = %@ \n", array1[1], [array1[1] class]); NSLog(@"\n obj1 = %p class = %@ \n", array2[1], [array2[1] class]); NSLog(@"\n obj1 = %p class = %@ \n", array3[1], [array3[1] class]);
输出:
2021-05-08 17:40:01.757406+0800 AlgorithmDemo[40202:14563979] array1 = 0x1006046e0 class = __NSArrayM array2 = 0x100604860 class = __NSArrayI array3 = 0x100604890 class = __NSArrayM ======== 元素是mutableString ======== obj0 = 0x100008220 class = __NSCFConstantString obj0 = 0x100008220 class = __NSCFConstantString obj0 = 0x100008220 class = __NSCFConstantString ======== 元素是String ======== obj1 = 0x100604510 class = __NSCFString obj1 = 0x100604510 class = __NSCFString obj1 = 0x100604510 class = __NSCFString
结论
- 集合类可变对象的mutableCopy和copy都是深拷贝
- 但集合内元素是浅拷贝
总结
非集合类对象只有不可变对象的copy是浅拷贝,其它都是深拷贝 集合类且集合内元素都是浅拷贝
也可换个方式总结:
- copy: 对不可变对象是浅拷贝,可变对象是深拷贝
- mutableCopy: 始终是深拷贝
- 无论深浅拷贝,集合对象内元素都是浅拷贝
集合里面元素拷贝(copyItems: )
集合浅拷贝
集合浅拷贝的方式很多,当创建一个浅拷贝时,之前集合里面的每个对象都会收到retain的消息,使其引用计数加1,并将其指针拷贝到新的集合中。
NSArray *shaoolwCopyArray = [someArray copyWithZone:nil]; NSDictionary *shaoolwCopyDict = [[NSDictionary alloc]initWithDictionary:someDict copyItems: NO];
数组浅拷贝
代码
NSArray *someArray = @[@222]; NSArray *shaoolwCopyArray = [someArray copyWithZone:nil]; NSLog(@"someArray addrerss:%p",someArray); NSLog(@"shaoolwCopyArray addrerss:%p",shaoolwCopyArray);
打印后的地址
浅拷贝的数组所指向的地址是一样的
这是因为对于数组我们只调用了它的copyWithZone方法,由于是不可变的数组,返回了自身,所以浅拷贝前后不可变数组内存地址不变
字典浅拷贝
代码
NSDictionary *someDict = @{@"11":@"22"}; NSDictionary *shaoolwCopyDict = [[NSDictionary alloc]initWithDictionary:someDict copyItems: NO]; NSLog(@"someDict addrerss:%p",someDict); NSLog(@"shaoolwCopyDict addrerss:%p",shaoolwCopyDict);
打印后的地址
字典所指向的内存地址发生了变化,其内部元素的地址不变。
而对于字典来说,shaoolwCopyDict是通过alloc和init 创建的,因此在内存中开辟了一段新的内存空间,所以浅拷贝前后字典的内存地址发生了变化.但对于之前字典中的对象,只是拷贝其内存地址,
如果代码改变一下
NSDictionary *someDict = @{@"11":@"22"}; NSDictionary *shaoolwCopyDict = [someDict copy];
打印结果
NSDictionary 它是不可变的使用copy 之后返回自身
浅拷贝总结
浅拷贝并不是自身的浅拷贝,而是对于其内部元素的浅拷贝
集合深拷贝
对于深拷贝将copyItems:换成YES就是深拷贝了。
在深拷贝中,系统会向集合中的每一个元素对象发送一个copyWithZone:消息,改消息是来自与NSCopying协议,如果有对象没有实现改协议方法,那么就会导致崩溃,如果实现了改方法,那么会根据该方法的具体实现。
数组深拷贝
数组深拷贝(元素不可变NSString)
代码NSString *str = @"2222"; NSArray *someArray = @[str]; NSArray *shaoolwCopyArray = [someArray copyWithZone:nil]; NSArray *deepCopyArray = [[NSArray alloc]initWithArray:someArray copyItems:YES]; NSLog(@"someArray addrerss:%p",someArray); NSLog(@"shaoolwCopyArray addrerss:%p",shaoolwCopyArray); NSLog(@"deepCopyArray addrerss:%p",deepCopyArray); NSLog(@"someArray[0] addrerss:%p",someArray[0]); NSLog(@"shaoolwCopyArray[0] addrerss:%p",shaoolwCopyArray[0]); NSLog(@"deepCopyArray[0] addrerss:%p",deepCopyArray[0]);
猜测结果shaoolwCopyArray和deepCopyArray应该是不一样的,这是因为deepCopyArray是通过alloc ,init 创建的,但最起码这两个数组的首元素地址是不一样的,运行后发现结果
运行结果
前三行与我们猜测的一致。但后面三行却 打印了相同的地址,然后后面也打印了一样的,明明采用了深浅拷贝,出现的结果确是相同的内存地址。
原因
集合类型的深拷贝会对每个元素调用copyWithZone:方法,
这就意味着刚刚后面三行打印的结果取决于该方法,在深拷贝时对于第一个元素调用了
NSString的copyWithZone:,但由于NSString是不可变的,对于其深拷贝创建一个新的内存是无意的,所以我们也可以猜测NSString的copyWithZone:返回的是self.所以浅拷贝时直接拷贝元素地址,而深拷贝时通过copyWithZone:方法来获取元素地址,两个结果是一样的。数组深拷贝(元素可变NSMutableString)
将数组里面的NSString元素改变为NSMutableString
代码
NSMutableString *str = [[NSMutableString alloc]initWithString:@"2222"]; NSArray *someArray = @[str]; NSArray *shaoolwCopyArray = [someArray copyWithZone:nil]; NSArray *deepCopyArray = [[NSArray alloc]initWithArray:someArray copyItems:YES]; NSLog(@"someArray addrerss:%p",someArray); NSLog(@"shaoolwCopyArray addrerss:%p",shaoolwCopyArray); NSLog(@"deepCopyArray addrerss:%p",deepCopyArray); NSLog(@"someArray[0] addrerss:%p",someArray[0]); NSLog(@"shaoolwCopyArray[0] addrerss:%p",shaoolwCopyArray[0]);
运行结果
集合完全拷贝
就是对对象的每一层都是重新创建的实例变量,不存在指针拷贝
例子(数组的归档解挡)
在对数组的归档解挡时就是完全深拷贝
NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];
三种集合拷贝总结
浅拷贝在浅拷贝操作时,对于被拷贝对象的每一层都是指针拷贝
深拷贝
在深拷贝操作时,对于被拷贝对象,至少有一层是深拷贝
完全拷贝
在完全拷贝操作时,对于被拷贝对象,每一层都是深拷贝
面试题
copy 修饰符
一般用于修饰字符串和block以及一些字典和数组。
copy 修饰(字符串/字典/数组)
看下面的例子
打印结果是NO,如果把copy换成strong就变成YES了
copy修饰符分析
在使用copy时,会对属性的setter方法进行判断,对属性copy,根据属性是否为可变,
- 可变返回一个新的不可变对象,即创建一个新不可变字符串
- 而对于不可变返回自身self,即为可变字符串。
strong修饰符分析
strong 则是对其引用,并没有执行copy方法
数组,字典,字符串,原理是一样的
对于NSString 作为不可变的字符串来说
1.修饰符copy返回的是自身,执行copy方法仍然返回自身。
2.strong 修饰也是返回自身,NSString 不可变的对象,使用strong和copy是一样的
copy 修饰(block)
在ARC中如果引用外部变量赋值时便会自动拷贝到内存中
ARC 下使用block 使用copy/strong无异
这是为什么,数组浅拷贝不变,而字典浅拷贝发生了变化???
这是因为对于数组我们只调用了它的copyWithZone方法,由于是不可变的数组,返回了自身,所以浅拷贝前后数组内存地址不变,而对于字典来说,shaoolwCopyDict是通过alloc和init
创建的,因此在内存中开辟了一段新的内存空间。但对于之前字典中的对象,只是拷贝其内存地址,所以浅拷贝前后字典的内存地址发生了变化,案例题(copyItems: 方法的讨论)
假设有一个Person类它有一个属性name,放入可变数组/或者不可变数组,数组发生深浅拷贝
代码
Person *a = [[Person alloc]init]; a.name = @"阿里巴巴"; Person *b = [[Person alloc]init]; b.name = @"今日头条"; NSMutableArray *mutA = [NSMutableArray arrayWithObjects:a,b, nil]; NSMutableArray *mutB = [mutA mutableCopy]; // Personerson @interface Person : NSObject @property (nonatomic, copy) NSString *name; @end
mutA 与 mutB 地址是否一样
NSLog(@"mutA addrerss:%p",mutA); NSLog(@"mutB addrerss:%p",mutB);
答案
mutA addrerss:0x60000371bba0 mutB addrerss:0x60000371be10 深拷贝数组地址不一样
mutA 与 mutB 里面元素的地址是否一样
NSLog(@"mutA[0] addrerss:%p",mutA[0]); NSLog(@"mutB[0] addrerss:%p",mutB[0]);
答案
mutA[0] addrerss:0x600003f6c3d0 mutB[0] addrerss:0x600003f6c3d0 里面元素的地址一样
mutA 与 mutB 当改变mutB 里面元素的name值,mutA 里面的值改变不
因为 mutA 与 mutB 里面的元素地址是一样的,所以改变 mutB 里面的值, mutA 也会被改变。
mutA 与 mutB 当改变mutB 里面元素的name值时,mutA 的值不变
copyItems: 等于YES
NSMutableArray *mutA = [NSMutableArray arrayWithObjects:a,b, nil]; NSMutableArray *mutB = [[NSMutableArray alloc] initWithArray:mutA copyItems:YES];
// Person - (id)copyWithZone:(NSZone *)zone { Person *person = [[Person allocWithZone:zone] init]; person.name = self.name; return person; }
copyItems: 等于YES 系统会向集合中的每一个元素对象发送一个copyWithZone:消息 而在copyWithZone: 的时候我创建了一个新对象,
数组的归档解挡
#import "Person.h" @implementation Person - (instancetype)initWithCoder:(NSCoder *)aDecoder { if (self = [super init]) { self.name = [aDecoder decodeObjectForKey:@"name"]; } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.name forKey:@"name"]; } @end
-
什么是深浅拷贝?如何实现深浅拷贝
2021-08-28 20:26:20什么是深浅拷贝?如何实现深浅拷贝 首先,先了解深浅拷贝的含义: 浅拷贝就是通过赋值的方式进行拷贝,那为什么说这是浅拷贝呢?就是因为赋值的方式只会把对象的表层赋值给一个新的对象,如果里面有属性值为数组或者...什么是深浅拷贝?如何实现深浅拷贝
首先,先了解深浅拷贝的含义:
浅拷贝就是通过赋值的方式进行拷贝,那为什么说这是浅拷贝呢?就是因为赋值的方式只会把对象的表层赋值给一个新的对象,如果里面有属性值为数组或者对象的属性,那么就只会拷贝到该属性在栈空间的指针地址,新对象的这些属性数据就会跟旧对象公用一份,也就是说两个地址指向同一份数据,一个改变就会都改变。
深拷贝则不会出现上述问题,引用数据类型,地址跟数据都会拷贝出来。
浅拷贝
让我们先来看看浅拷贝,代码走你
let person = { uname: '张三', age: 22, sex: '男', arr: ['小明', '大大', '小小'], obj: { index: 1, name: '三和' }, say: function () { console.log('hello javascript') } } let son = { ...person } person.arr.push('小王') console.log(son) console.log(person)
控制台打印结果
一个对象的属性是引用类型,那么改变该属性值里面的内容,另外的拷贝对象也会改变,因此这种拷贝是浅拷贝
深拷贝
1.通过递归函数来实现深拷贝
let person = { uname: '张三', age: 22, sex: '男', arr: ['小明', '大大', '小小'], obj: { index: 1, name: '三和' }, say: function () { console.log('hello javascript') } } let son = {} function myCopy(newObj, obj) { for(let key in obj) { if (obj[key] instanceof Array) { newObj[key] = [] myCopy(newObj[key], obj[key]) } else if (obj[key] instanceof Object) { newObj[key] = {} myCopy(newObj[key], obj[key]) } else { newObj[key] = obj[key] } } } // 调用递归函数 myCopy(son, person) person.arr.push('小王') console.log(son) console.log(person)
控制台打印结果
2.通过 json 反序列化实现深拷贝
let person = { uname: '张三', age: 22, sex: '男', arr: ['小明', '大大', '小小'], obj: { index: 1, name: '三和' }, say: function () { console.log('hello javascript') } } function myCopy(obj) { let _obj = JSON.stringify(obj) let newObj = JSON.parse(_obj) return newObj } let son = myCopy(person) person.arr.push('小王') console.log(son) console.log(person)
控制台打印结果
3.通过 jQuery 封装的方法($.extend())实现深拷贝
必须先引入 jQuery
第一个参数必须是 truelet person = { uname: '张三', age: 22, sex: '男', arr: ['小明', '大大', '小小'], obj: { index: 1, name: '三和' }, say: function () { console.log('hello javascript') } } let son = {} $.extend(true, son, person) // 通过 $.extend() 方法实现深拷贝 // 第一个参数必须是 true person.arr.push('小王') console.log(son) console.log(person)
控制台打印结果
从三种深拷贝的方法打印结果中可以发现一个问题,那就是在三个 person 对象中,都定义了一个 say 方法,前两种拷贝方法,都无法拷贝对象里面的方法,第三种方法却可以,所以使用的时候应该考虑到这个因素
今天的分享就到这里了
-
详解python深浅拷贝区别
2021-01-01 04:42:31深浅拷贝的作用 1,减少内存的使用 2,以后在做数据的清洗、修改或者入库的时候,对原数据进行复制一份,以防数据修改之后,找不到原数据。 浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象。 深 -
js 深浅拷贝原理
2022-01-24 17:05:14'arr1-1') //[1,2,33,4,5] 'arr1-1' console.log(arr,'arr-1') //[1,2,33,4,5] 'arr-1' arr2 = 4 console.log(arr2,'arr2-1') //4 arr2-1 console.log(arr,'arr-2') //[1,2,33,4,5] 'arr-2' 4:深浅拷贝:针对引用...1:数据类型:
① 基本数据类型:Number、string、null、undefined、boolean
② 引用数据类型: Object =>{object、Array、Function}2:栈(stack)、堆(heap):
① 基本数据类型是直接存储在栈中的数据,栈的存取速度比堆要快,存在栈中的数据大小是确定的
问:为什么基本数据类型存储在栈中? 存储结构不一样,引用数据类型一般占空间大
答:(http://www.javashuo.com/article/p-gkhlbedc-gg.html)
问:基本数据类型一定存在栈中么?
答:(https://www.cnblogs.com/linliquan/p/11273932.html)
② 引用数据类型存储的是该对象在栈中引用,真实的数据存放在堆内存里3:基本数据类型和引用数据类型最大的区别:传值和传址
let arr = [1, 2, 3, 4, 5] //原数据(引用数据类型) let arr1 = arr console.log(arr,'arr') //[1,2,3,4,5] 'arr' console.log(arr1,'arr1') //[1,2,3,4,5] 'arr1' let arr2 = arr[2] console.log(arr2,'arr2') //3 arr2 arr1[2] = 33 console.log(arr1,'arr1-1') //[1,2,33,4,5] 'arr1-1' console.log(arr,'arr-1') //[1,2,33,4,5] 'arr-1' arr2 = 4 console.log(arr2,'arr2-1') //4 arr2-1 console.log(arr,'arr-2') //[1,2,33,4,5] 'arr-2'
4:深浅拷贝:针对引用数据类型
① 浅拷贝:只复制指向某个对象的指针而不复制对象本身,新旧对象还是共享同一块内存。
② 深拷贝:指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。5:对象的直接赋值和浅拷贝的区别:
① 直接赋值:将一个对象赋值给一个新的对象的时候,赋的其实是该对象在栈中的地址,而不是堆中的数据。
也就是一个对象的改变就会改变另外一个对象。
② 浅拷贝:会创建一个对象,再去遍历原始对象,如果原对象的属性值是基础类型,那么就拷贝基础类型,
如果是引用类型,则拷贝的是指针。let arr =[11,22,33,44,55] let arr1 = arr //直接赋值 let arr2 = [...arr] //扩展运算符实现浅拷贝:...arr=>11,22,33,44,55
6:浅拷贝的实现方式:
① Object.assign():可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。
注:当object只有一层的时候,是深拷贝let query = {name:'原数据',content:['前端','学习']} let query1 = Object.assign({}, query) console.log(query,'query') //{name:'原数据',content:['前端','学习']} query console.log(query1,'query1') //{name:'原数据',content:['前端','学习']} query1 query1.name = '浅拷贝' query1.content[1] = '浅拷贝学习' console.log(query,'query-1') //{name:'原数据',content:['前端','浅拷贝学习']} query-1 console.log(query1,'query1-1') //{name:'浅拷贝',content:['前端','浅拷贝学习']} query1-1
② Array.prototype.concat()
let query = [1,3,{userName:'浅拷贝'}] let query1 = query.concat() query1[1] = 33 query1[2].userName = 'concat' console.log(query,'query')//[1,3,{userName:'concat'}] console.log(query1,'query1')//[1,33,{userName:'concat'}]
③ Array.prototype.slice()
let query = [1,3,{userName:'浅拷贝'}] let query1 = query.slice(0) query1[1] = 33 query1[2].userName = 'concat' console.log(query,'query')//[1,3,{userName:'concat'}] console.log(query1,'query1')//[1,33,{userName:'concat'}]
说明:Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。
浅拷贝的方式有很多:for…in 等等var person = { name: "浅拷贝", age: 111, sex: '保密' }; var person2 = {}; for (var key in person1) { person2[key] = person1[key]; } console.log(person2) //{name: "浅拷贝", age: 111, sex: '保密'}
7:深拷贝的实现方式:
① JSON.parse(JSON.stringify()): 用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象, 一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。
说明:JSON.parse(JSON.stringify())自身存在小问题:比如:不能处理函数,
因为 JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个JSON字符串,不能接受函数。
另外还有 将undefined,’ ',empty 都转换为null,let arr = ['深拷贝',' ','',null,undefined, ,{userName:'深拷贝1'},function(){}] let arr1 = JSON.parse(JSON.stringify(arr)) console.log(arr1,'arr1') arr1[6].userName = '拷贝' console.log(arr,'arr') console.log(arr1,'arr1-1')
• 如果对象中存在循环引用的情况也无法正确实现深拷贝。
const a = { b: 1, } a.c = a; JSON.parse(JSON.stringify(a));
• 如果 data 里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象。
const a = { b: new Date(1536627600000), } console.log(a) //{b: Tue Sep 11 2018 09:00:00 GMT+0800 (GMT+08:00)} console.log(JSON.parse(JSON.stringify(a)))//{b: '2018-09-11T01:00:00.000Z'}
• 如果 data 里有RegExp、Error对象,则序列化的结果将只得到空对象;
const a = { b: new RegExp(/\d/), c: new Error('错误') } console.log(a) // console.log(JSON.parse(JSON.stringify(a)))
• 如果 data 里有NaN、Infinity和-Infinity,则序列化的结果会变成null
const a = { b: NaN, c: 1.7976931348623157E+10308, d: -1.7976931348623157E+10308, } console.log(JSON.parse(JSON.stringify(a)))//{b: null, c: null, d: null}
② 手写递归方法:
递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。function deepClone(target){ if(target !== null && typeof target === 'object'){ //判断 target 是不是引用类型。 let result = Object.prototype.toString.call(target) === "[object Array]" ? [] : {}; for (let k in target){ //hasOwnProperty表示是否有自己的属性。这个方法会查找一个对象是否有某个属性,但是不会去查找它的原型链。 if (target.hasOwnProperty(k)) { result[k] = deepClone(target[k]) } } return result; }else{ return target; } }
• hasOwnProperty(链接跳转至详情:https://blog.csdn.net/i_dont_know_a/article/details/84324051)
/**ruoyi * This is just a simple version of deep copy * Has a lot of edge cases bug * If you want to use a perfect deep copy, use lodash's _.cloneDeep */ function deepCopy(source) { if (!source && typeof source !== 'object') { 判断source 是不是引用类型。 throw new Error('error arguments', 'deepClone') } const targetObj = source.constructor === Array ? [] : {} Object.keys(source).forEach((keys) => { if (source[keys] && typeof source[keys] === 'object') { targetObj[keys] = deepCopy(source[keys]) } else { targetObj[keys] = source[keys] } }) return targetObj } // 测试 var obj = { //原数据,包含字符串、对象、函数、数组等不同的类型 name: 'test', main: { a: 1, b: 2, }, fn: function () {}, friends: [1, 2, 3, [22, 33]], } let query = deepCopy(obj) query.friends[2]=4 console.log(obj) console.log(query)
function deepClone(target) { // 获取数据类型 function getType(target) { return Object.prototype.toString.call(target) } //判断数据是不是引用类型 function isObject(target) { return target !== null && (typeof target === 'object' || typeof target === 'function'); } //处理不需要遍历的应引用类型数据 function handleOherData(target) { const type = getType(target); switch (type) { case "[object Date]": return new Date(target) case "[object RegExp]": return cloneReg(target) case "[object Function]": return cloneFunction(target) } } //拷贝Symbol类型数据 function cloneSymbol(targe) { const a = String(targe); //把Symbol字符串化 const b = a.substring(7, a.length - 1); //取出Symbol()的参数 return Symbol(b); //用原先的Symbol()的参数创建一个新的Symbol } //拷贝正则类型数据 function cloneReg(target) { const reFlags = /\w*$/; const result = new target.constructor(target.source, reFlags.exec(target)); result.lastIndex = target.lastIndex; return result; } //拷贝函数 function cloneFunction(targe) { //匹配函数体的正则 const bodyReg = /(?<={)(.|\n)+(?=})/m; //匹配函数参数的正则 const paramReg = /(?<=\().+(?=\)\s+{)/; const targeString = targe.toString(); //利用prototype来区分下箭头函数和普通函数,箭头函数是没有prototype的 if (targe.prototype) { //普通函数 const param = paramReg.exec(targeString); const body = bodyReg.exec(targeString); if (body) { if (param) { const paramArr = param[0].split(','); //使用 new Function 重新构造一个新的函数 return new Function(...paramArr, body[0]); } else { return new Function(body[0]); } } else { return null; } } else { //箭头函数 //eval和函数字符串来重新生成一个箭头函数 return eval(targeString); } } /** * 遍历数据处理函数 * @array 要处理的数据 * @callback 回调函数,接收两个参数 value 每一项的值 index 每一项的下标或者key。 */ function handleWhile(array, callback) { let index = -1; const length = array.length; while (++index < length) { callback(array[index], index); } } function clone(target, map) { if (isObject(target)) { let result = null; if (getType(target) === "[object Array]") { result = [] } else if (getType(target) === "[object Object]") { result = {} } else if (getType(target) === "[object Map]") { result = new Map(); } else if (getType(target) === "[object Set]") { result = new Set(); } // 解决循环引用 if (map.has(target)) { return map.get(target); } map.set(target, result); if (getType(target) === "[object Map]") { target.forEach((value, key) => { result.set(key, clone(value, map)); }); return result; } else if (getType(target) === "[object Set]") { target.forEach(value => { result.add(clone(value, map)); }); return result; } else if (getType(target) === "[object Object]" || getType(target) === "[object Array]") { const keys = getType(target) === "[object Array]" ? undefined : Object.keys(target); function callback(value, key) { if (keys) { // 如果keys存在则说明value是一个对象的key,不存在则说明key就是数组的下标。 key = value } result[key] = clone(target[key], map) } handleWhile(keys || target, callback) } else { result = handleOherData(target) } return result; } else { if (getType(target) === "[object Symbol]") { return cloneSymbol(target) } else { return target; } } } let map = new WeakMap; const result = clone(target, map); map = null; return result }
loadsh:_.cloneDeep()
jQuery中extend( )let obj = { childs: ['Jack', 'Tom'], age: 45 } let newObj = $.extend(true, {}, obj) newObj.childs[0] = 'Jone' console.log(newObj.childs[0]) //----'Jone' console.log(obj.childs[0]) //-----'Jack'
8.总结:
-
JS最完整的深浅拷贝实现方式
2022-03-03 15:21:32深浅拷贝: 内存中一共分为栈内存和堆内存两大区域,所谓深浅拷贝主要是对js引用类型数据进行拷贝一份,浅拷贝就是引用类型数据相互赋值之后,例obj1=obj2;如果后面的操作中修改obj1或者obj2,这个时候数据是会进行... -
详细分析JavaScript中的深浅拷贝
2021-01-18 16:49:31在说JS中深浅拷贝之前,我们需要对JS中的数据类型有所了解,分为基本数据类型与引用数据类型,对于基本数据类型并没有深浅拷贝的说法,深浅拷贝主要针对引用数据类型。 一、浅拷贝 浅拷贝只复制了引用,并没有复制... -
C++什么是深浅拷贝,深浅拷贝的区别?
2020-08-26 16:10:28关于C++深浅拷贝的示例浅拷贝深拷贝的写法完整程序运行结果 浅拷贝 // 浅拷贝: class String { public: String(const char *str) : _str(new char[strlen(str) + 1]) { strcpy(_str, str); } String(const ... -
Python的深浅拷贝讲解!
2020-12-11 15:18:34↑↑↑关注后"星标"Datawhale每日干货&每月组队学习,不错过 Datawhale干货作者:皮钱超,厦门大学,...前言在很多语言中都存在深浅拷贝两种拷贝数据的方式,Python中也不例外。本文中详细介绍了Python中的深... -
深浅拷贝
2020-03-22 17:48:41深浅拷贝是存在于复杂数据类型中的; 基本数据类型不存在:当两个变量赋值相同时,只是储存的内容相同,可以通过变量名称来单独操作其中一个,另一个不它的受影响。 而复杂数据类型,当两个变量赋值相同时,想单独... -
实现JS深浅拷贝的五种方式
2022-04-22 10:22:46在了解深浅拷贝之前,我们需得对堆栈、基本数据类型、引用数据类型有基本的了解 基本数据类型:number、string、boolean、null、undefined 引用数据类型:object、function、array 栈内存是保存大小固定的数据,即... -
python之深浅拷贝(最详细讲解)
2020-11-10 20:01:12浅拷贝: 只复制最外层的结构,除最外层其余的直接将其地址引用过来。 深拷贝: 全部数据/结构都进行复制,(除不可变类型)增大数据独立性 ,如果深拷贝中,只要发现复制数据中有一个不是不可变类型就重新创建。 ... -
JS之深浅拷贝详解
2021-09-13 16:55:521、深拷贝与浅拷贝的区别 如何区分深拷贝与浅拷贝,简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝;如果B没变,那就是深拷贝。 2、栈堆、基本数据类型、引用数据... -
JavaScript的深浅拷贝解析及手写深拷贝实现
2022-01-13 17:37:031. 说到JavaScript的深浅拷贝,那么必然得从其‘源头’说起----JavaScript的两大数据类型(基本数据类型和引用数据类型)。 基本数据类型的特点:数据直接存储在栈中。 引用数据类型的特点:存储在栈中的是对象的...