在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带来的困扰,也避免了在遍历的时候因相互调用对象导致的情况。 用一张话来结束深拷贝和浅拷贝的区别: 浅拷贝就是新旧对象引用同一个内存空间,一个改变则全部改变,即:一变全变;深拷贝就是旧对象引用原来的空间,新对象则新建空间,自己控制自己的大小。