数据类型
数据分为基本数据类型(String,Number,Boolean,Null,Undefined,Symbol)和对象数据类型。
- 基本数据类型的特点:直接存储在栈(stack)中的数据
- 引用数据类型的特点:存储的是该对象在栈(Stack)中引用,真实的数据存放在堆(Heap)内存里
引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
浅拷贝(Shallow Copy)与深拷贝(Deep Copy)
深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。而对于值类型而言,所有的拷贝都是深拷贝。
浅拷贝(Shallow Copy)
浅拷贝会基于原对象,在堆或者栈中实例化一个新的对象,而对于该新对象中的字段(field)为不同数据类型时,会进行不同的处理:
- 当字段为
基本数据类型
,该字段会被复制一份,并作为新对象的对应字段。 - 当字段为
对象数据类型
,该对象在堆中的地址会被复制,并作为新对象的对应字段的值(此过程中,不存在新对象的实例化)。因此,新旧对象对应的这个字段都指向堆内存中同一个区域。
深拷贝(Deep Copy)
与浅拷贝类似,深拷贝也会基于原对象,在堆或者栈中实例化一个新的对象。而不同点在,对于该新对象中的字段(field)为不同数据类型时,都会进行同样的处理,即:
- 当字段为
基本数据类型
,该字段会被复制一份,并作为新对象的对应字段。 - 当字段为
对象数据类型
,该字段指向的对象会被重新创建一个,新创建的对象副本(在堆中的地址)作为新对象的对应字段的值
赋值和浅拷贝的区别
赋值
当我们把一个对象(或称为”引用数据类型“)赋值给一个新的变量时,赋的其实是该对象指向于堆中的那个地址值,而不是堆中的数据。也就是两个对象指向的是同一个存储在堆的内存空间。
因此,无论引用哪个变量,其实都是改变的这个内存空间的内容。所以,两个对象是联动的。
浅拷贝
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。
如果属性是基本类型
,拷贝的就是基本类型的值;如果属性是内存地址
(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。
例子
对象赋值
// 对象赋值
var obj1 = {
'name' : 'zhangsan',
'age' : '18',
'language' : [1,[2,3],[4,5]],
};
var obj2 = obj1;
obj2.name = "lisi";
obj2.language[1] = ["二","三"];
console.log('obj1',obj1)
console.log('obj2',obj2)
运行结果
分析
可以看到,由于 var obj2 = obj1;
的本质是将obj1
所指向的对象的内存地址赋值给obj2
,因此无论使用obj1
还是obj2
,本质上都是在操作同一块堆内存。
浅拷贝
情况1
// 浅拷贝1
var obj1 = {
'name' : 'zhangsan',
'age' : '18',
'language' : [1,[2,3],[4,5]],
};
var obj3 = shallowCopy(obj1);
obj3.name = "lisi";
obj3.language = ["一","二","三"];
console.log('obj1',obj1)
console.log('obj3',obj3)
function shallowCopy(src) {
var dst = {};
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
dst[prop] = src[prop];
}
}
return dst;
}
运行结果
分析
对于 obj3.language = ["一","二","三"];
而言,obj3.language
是一个引用数据类型。因此,最终obj3
对象的language
字段会指向["一","二","三"]
所在的堆内存。而obj1
对象的language
字段仍然指向[1,[2,3],[4,5]]
所在的堆内存,因此值不会被改变。
情况2
// 浅拷贝2
var obj1 = {
'name' : 'zhangsan',
'age' : '18',
'language' : [1,[2,3],[4,5]],
};
var obj3 = shallowCopy(obj1);
obj3.name = "lisi";
obj3.language[1] = ["二","三"];
function shallowCopy(src) {
var dst = {};
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
dst[prop] = src[prop];
}
}
return dst;
}
console.log('obj1',obj1)
console.log('obj3',obj3)
运行结果
分析
对于 obj3.language[1] = ["二","三"];
:
-
由于
language
字段为一个引用数据类型,因此obj1
和obj3
的language
字段均指向同一个堆内存地址。 -
所以
obj1.language[1]
和obj3.language[1]
均指向同一个堆内存地址。 -
最终,
obj3.language[1] = ["二","三"];
的执行会改变obj1.language[1]
和obj3.language[1]
的值。
Reference
- https://en.wikipedia.org/wiki/Object_copying
- 浅拷贝与深拷贝 - http://web.jobbole.com/95554/
- 细说 Java 的深拷贝和浅拷贝 - https://www.cnblogs.com/plokmju/p/7357205.html