在js的语法中,像Number,String,Boolean这样的基本类型,它们的传值方式是按值传递的,而想对象{a: 10, b: 20},它们的传值是引用传值的
对于对象来说,在这里就总结一下深拷贝和浅拷贝时遇到的问题。
基本类型的按值传递,比如:a = 10, b = a,系统会为a和b 分配不同的内存空间,彼此之间相互不影响。
var a = 10;
var b = a;
b = 20;
console.log(a); //输出结果为10
console.log(b); //输出结果为20
但是对于对象来说,结果就截然相反。
浅拷贝:
var obj1 = {
a: 10,
b: {
a: 'yf',
b: 'bl'
},
c: ['Bob','Tom','nick'],
d: function(){
console.log('Hello World');
}
};
var obj2 = obj1;
obj2.a = 30;
console.log(obj1);
console.log(obj2);
console.log(obj1 === obj2);
输出结果如下:
由上面的代码块我可以很清楚的看到,obj1、obj2引用了同一块内存空间,虽然我们对我obj2 进行了赋值操作,相当于给整个内存空间进行了赋值,所以obj1、obj2都进行了修改,这个就是所谓的浅拷贝。
深拷贝:
1、使用JSON数据解析实现深拷贝。
// 使用JSON数据解析来实现深度拷贝(JSON不能够识别Function类型)
var obj1 = {
a: 10,
b: {
a: 'yf',
b: 'bl'
},
c: ['Bob','Tom','nick'],
d: function(){
console.log('Hello World');
}
};
function deepClone(obj){
return JSON.parse(JSON.stringify(obj));
}
var e = deepClone(obj1);
e.a = 20;
console.log(e);
console.log(obj1);
// 通过类型检测判断JSON是不识别Function类型的
console.log(typeof e.d);
console.log(typeof obj1.d);
输出的结果如下所示:
使用JSON数据解析的这种方式是比较容易理解的,我们使用JSON.stringify将其转化为JSON字符串,这个时候重新生成的字符串和原来的对象是没有什么关系的,也就是说为字符串重新开辟了一个内存空间,然后我们使用JSON.parse将其转化为对象,此时在新旧对象上的操作是彼此独立的,所以我们输出的结果中a的值是不同的。
**缺点:**这种方法存在一个致命性的错误,不能够识别对象中的Function类型,JSON.stringify()不能够识别Function,会返回undefined,所以这个方法只能用于只有数据的对象中。这个方法会抛弃对象的constructor,深拷贝之后,不管对象的构造函数是什么,都会将其变成Object。
为了解决这个问题,我们采用递归的方法来对 对象中的属性进行遍历输出。
2、递归拷贝
function deepClone(Obj1, Obj2) {
var obj = Obj2 || {};
for (var i in Obj1) {
var prop = Obj1[i];
// 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : {};
arguments.callee(prop, obj[i]);
} else {
obj[i] = prop;
}
}
return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21, c: function(){alert('hello world');}}};
deepClone(obj, str);
console.log(str.a);
采用递归的方法,我们成功的解决了Function带来的困扰,也避免了在遍历的时候因相互调用对象导致的情况。
用一张话来结束深拷贝和浅拷贝的区别:
浅拷贝就是新旧对象引用同一个内存空间,一个改变则全部改变,即:一变全变;深拷贝就是旧对象引用原来的空间,新对象则新建空间,自己控制自己的大小。