上图是对象在内存中存储的情况,我们可以明显的知道obj和obj2是共享同一片地址空间的,如果对其中的任何一个进行修改,都会影响到指向同一片地址空间的其他变量。但是在很多情况下,我们需要复制出来完全一样的对象的内容,这就涉及到拷贝。
拷贝的分类
- 浅拷贝
- 深拷贝
要说两者的区别,还得看被复制对象的复杂程度。
let obj1 = {
name: 'mzy',
age: 20
}
let obj2 = {
name: 'mzy',
nickName: ['mzt','zyd']
}
如果是第一种情况,即对象中的value,都是基本数据类型,那么深拷贝和浅拷贝的结果是完全一致的。
如果是第二种情况,对于浅拷贝,value是基本数据类型是复制的,但是如果value也是对象(数组也属于对象),那么复制的对象和原对象还是共享同一片地址空间的,而深拷贝则是完全不同的地址空间。(即浅拷贝只是解决了对象第一层包裹的问题,多层包裹的情况没有解决)
当然,对于浅拷贝的难度相对于深拷贝难度不是很大,深拷贝需要考虑多种情况,我也写不出来完美的深拷贝。
浅拷贝
Object.assign(target,…sources)
Object.assign()可以接受多个参数,第一个参数是目标对象,2-N个参数是需要复制的对象
let obj = {
name: 'mzy',
age: 20
}
let shallowCopy = Object.assign({},obj);
当然我这里只是简单的提及一下方法,其实还有很多其他情况,有兴趣的同学可以看阮一峰老师的es6或者是MDN有关文档
阮一峰老师的es6入门MDN文档
展开运算符(…)
这个是es6提供的方法,用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
let obj = {
name: 'mzy',
age: 20
}
let arr = [1,2,3,4];
let shallowCopy1 ={...obj}
let shallowCopy2 = [...arr];
手动实现
当然自己实现一个浅复制
function shallowCopy(target){
let obj = target instanceof Array ? [] : {};
for(let i in target){
obj[i] = target[i];
}
return obj;
}
深拷贝
JSON.parse(JSON.stringify(target))
这个是经常使用的一种方法,但是有局限性
- 会忽略undefined
- 不能序列化函数
- 不能解决循环引用的对象
let obj = {
name: 'mzy',
nickName: ['mxt','zyd'],
age: undefined,
getName: function(){
return this.name;
}
}
let deepCopy = JSON.parse(JSON.stringify(obj)); // {name: "mzy" nickName: (2) ["mxt", "zyd"]}
// 无法解决第三点
let obj = {
a: 1
b: {
c: 2,
d: 3,
},
}
obj.c = obj.b
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c
let deepCopy = JSON.parse(JSON.stringify(obj)); // 报错:Uncaught SyntaxError: Unexpected identifier
手动实现
当然自己也可以手动实现一个简单的深复制
// 只考虑Array和Object
function checkType(target){
return Object.prototype.toString(target).slice(8,-1);
}
function deepCopy(target){
let result = null;
let targetType = checkType(target);
if(targetType === "Object"){
result = {}
}else if(targetType === "Array"){
result = []
}else{
return target;
}
for(let i in target){
let value = target[i];
if(checkType(value) === "Object" || checkType(value) === "Array"){
result[i] = deepCopy(target)
}else{
result[i] = value;
}
}
return result;
}
其实这只是一个最简单的深拷贝,深拷贝还需要考虑的情况很多,例如:循环应用、Symbol、new Date等多种复杂情况。