1、数据类型
1.1 数据类型分类
ECMAScript中的可以分为两大类:
1、基本数据类型:名值存储在栈内存中,包括:Number、String、Boolean、Null、 Undefined。
2、引用数据类型:名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址
指向堆内存中的值,包括:Object(数组Array和函数function都属于Object)。
深拷贝和浅拷贝的概念只存在于引用类型中,因为基本数据类型会直接把值拷贝给另一个变量,并没有涉及到引用。
1.2 对象分类
第一类对象:属性全是基本数据类型
var object1 = {
a: 1,
b: 2
};
第二类对象:属性中存在引用数据类型(不包含函数function)
var object2 = {
a: 1,
b: 2,
c: {
d: 4
}
};
第三类对象:属性中包含引用类型(包括函数function)
var object3 = {
a: 1,
b: 2,
c: {
d: 4
},
e: function(){
console.log('this is function e in object3!')
}
}
2、赋值
所谓赋值,就是使用" = "。现在使用第一类对象object1。
var object1 = {
a: 1,
b: 2
};
var copyObject1 = object1;
object1.a = 100;
console.log('改变object1之后的copyObject1',copyObject1);
可以看到,object1.a = 100;
后,copyObject1也会改变,因为它们两个指向同一地址。所以object1 === copyObject1 true
。对于第二类和第三类对象来说,结果肯定是二者也会互相影响。
3、浅拷贝
赋值会导致两个对象指向同一地址,二者会互相影响对方,在有的时候,我们并不希望二者之间互相影响。通过Object.assign
函数,可以实现浅拷贝。
直接看例子:
var object1 = {
a: 1,
b: 2
};
var copyObject1 = Object.assign({},object1);
object1.a = 100;
console.log('object1: ',object1);
console.log('copyObject: ',copyObject1);
console.log('object1 === copyObject1',object1 === copyObject1)
可以看到,貌似object1和copyObject1已经没有关系了。但是!事实并非如此,不然为什么叫浅
拷贝呢?
现在我们拷贝第二类对象:
var object2 = {
a: 1,
b: 2,
c: {
d: 4
}
};
var copyObject2 = Object.assign({},object2);
object2.c.d = 100;
console.log('浅拷贝后的copyObject2: ',copyObject2)
console.log('object2 === copyObject2',object2 === copyObject2);
console.log('object2.c === copyObject2.c',object2.c === copyObject2.c);
object2 === copyObject2 false
而 object2.c === copyObject2.c true
,可见Object.assign
不会将第二次的应用类型拷贝,也就是引用类型数据的引用类型数据属性
。所以对于只包含基本数据类型属性的对象,进行浅拷贝就可以达到我们的要求,但是对于多层的复杂对象,只能进行深拷贝来实现解除对象之间的关联。
4、深拷贝
4.1 使用JSON.stringfy和JSON.parse
var object2 = {
a: 1,
b: 2,
c: {
d: 4
}
};
var copyObject2 = JSON.parse(JSON.stringify(object2));
object2.c.d = 100;
console.log('object2',object2);
console.log('深拷贝后的copyObject2',copyObject2);
console.log('object2 === copyObject2',object2 === copyObject2);
console.log('object2.c === copyObject2.c',object2.c === copyObject2.c);
JSON.parse(JSON.stringify(object2))
是存在缺陷的,当遇到第三类对象时,并不会将函数属性拷贝到目标对象中。
var object3 = {
a: 1,
b: 2,
c: {
d: 4
},
e: function(){
console.log('this is function e in object3!')
},
array: [1,2,3,4]
}
console.log(object3);
object3.e();
var copyObject3 = JSON.parse(JSON.stringify(object3));
console.log('深拷贝后的copyObject3',copyObject3);
4.2 手写递归函数实现深拷贝
var object3 = {
a: 1,
b: 2,
c: {
d: 4
},
e: function(){
console.log('this is function e in object3!')
},
array: [1,2,3,4]
}
console.log('object3: ',object3);
var copyObject3 = deepCopy(object3);
console.log('深拷贝后的copyObject3',copyObject3);
function deepCopy(source) {
if (typeof source !== 'object') return source
//判断是对象还是数组
let res = source.constructor === Array ? [] : {}
for (var key in source) {
// hasOwnProperty 这个方法会查找一个对象是否有某个属性,但是不会去查找它的原型链。
if (source.hasOwnProperty(key)) {
//考虑对象属性可能是 undefined 和 null 的情况
if (source[key]) {
if (source[key].constructor === Object) {
res[key] = deepCopy(source[key])
} else {
res[key] = source[key]
}
} else {
res[key] = source[key]
}
}
}
return res
}
基本原理就是通过判断对象的属性,如果是引用类型,那么就要继续递归拷贝。当不是引用类型时,直接返回,结束递归。