JS中的变量类型分为 基本类型 和 引用类型;对基本类型进行复制操作会对值进行一份拷贝,而对引用类型赋值,则会进行地址的拷贝,最终两个变量指向同一份数据。
// 基本类型
var a = 1;
var b = a;
a = 2;
console.log(a, b); // 2, 1 ,a b指向不同的数据
// 引用类型:指向同一份数据
var a = {c: 1};
var b = a;
a.c = 2;
console.log(a.c, b.c); // 2, 2 全是2,a b指向同一份数据
其实深拷贝和浅拷贝都是针对的引用类型。浅拷贝就是只进行一层拷贝,深拷贝就是无限层级拷贝
。
例:
(先忽略shadowCopy\deepCopy的具体实现)
var a1 = {b: {c: {}};
var a2 = shadowCopy(a1); // 浅拷贝
a2.b.c === a1.b.c // true
var a3 = deepCopy(a1); // 深拷贝
a3.b.c === a1.b.c // false
一、浅拷贝
//对象浅拷贝
function shadowCopy(obj){
if(typeof obj !== 'object') return ;
var newObj;
if(obj.constructor === Array){
newObj = [];
} else {
newObj = {};
newObj.constructor = obj.constructor;//保留对象的constructor属性
}
for(var prop in obj){
if(obj.hasOwnProperty(prop)){
newObj[prop] = obj[prop];
}
}
return newObj;
}
var obj = {arr:[1,2],name:'bty'};
var obj2 = shadowCopy(obj);
console.log(obj.arr[0]); //1
obj2.arr[0] = 9;
console.log(obj.arr[0]); //9
上述代码实现了浅拷贝,会有不能正确实现数组的浅拷贝
和拷贝操作丢失了对象的constructor属性
的问题。
二、深拷贝
那么深拷贝可能就需要层层递归,复制对象的所有属性,包括对象属性的属性的属性,有人想出了用JSON的解析实现,如下代码:
function deepCopy(obj){
if(typeof obj !== "object"){ return ;}
var str = JSON.stringify(obj);
return JSON.parse(str);
}
上面的方法不适用的条件是
- 需要考虑把函数,正则等特殊数据类型复制
- 当前对象不支持JSON
- JSON复制会忽略掉值为undefined以及函数表达式
可以看下面的栗子:
var obj = {
a: 1,
b: 2,
c: undefined,
sum: function() { return a + b; }
};
var obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2);//输出:Object {a: 1, b: 2}
这个时候还是要层层递归来不同情况不同分析来考虑的,最终的方案如下:
//对象深拷贝
function deepCopy(obj){
var newObj = obj.constructor === Array ? []:{};
newObj.constructor = obj.constructor;
if(typeof obj !== "object"){
return ;
} else if(window.JSON){
newObj = JSON.parse(JSON.stringify(obj));//若需要考虑特殊的数据类型,如正则,函数等,需把这个else if去掉即可
} else {
for(var prop in obj){
if(obj[prop].constructor === RegExp ||obj[prop].constructor === Date){
newObj[prop] = obj[prop];
} else if(typeof obj[prop] === 'object'){
newObj[prop] = deepCopy(obj[prop]);//递归
} else {
newObj[prop] = obj[prop];
}
}
}
return newObj;
}
var obj = {arr:[1,2],name:'bty'};
var obj2 = deepCopy(obj);
console.log(obj.arr[0]); //1
obj2.arr[0] = 9;
console.log(obj.arr[0]); //1
注:Object.assign()是深拷贝