写前端的时候经常会遇到对象的拷贝,一般我们会用到深拷贝,深拷贝就是完完整整的将一个对象从内存中拷贝一份出来,放到另一块新开辟的内存中去。向下面这种赋值是浅拷贝,a、b都是对同一块内存进行引用,a、b哪一个变量修改对象的属性值都会相互影响。总结一下常用的对象深拷贝以及数组对象的深拷贝。
var a = {id:"",name:""}
var b = a;
1. 序列化转成json字符串深拷贝,以及存在的问题
2. Object.assign()深拷贝,以及存在的问题
3. 循环递归深拷贝
一、序列化转成json字符串深拷贝
例:
let source = {"id":1};
//序列化转成json字符串
let jsonStr = JSON.stringify(source)
//反序列化为对象
let target = JSON.parse(jsonStr);
存在的问题:
此方法仅在原对象包含可序列化值类型且没有任何循环引用时才有效。不可序列化值类型的一个例子是 Date 对象 - JSON.parse 只能将其解析为字符串而无法解析回其原始的 Date 对象
注:
同时总结java的序列化
序列化:就是把一个java对象转成字节流
反序列化:就是把字节流转成java对象
当然序列化不仅仅可以是转成字节流或者json字符串,还有很多种方式
为什么在java对象要序列化,什么场景要序列化?
如果没有序列化,怎么把一个在内存里面的对象保存到文件里面去,怎么把内存的对象通过网络传输到另一台计算机的内存去呢?序列化就是把内存的对象转成字节流或者json字符串等方式进行传输用的,用在一些保存对象到文件、网络传输对象等io流传输对象的场景。
二、Object.assign()深拷贝
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
基本语法:Object.assign(target, …sources)
例:
var source = {"id":1,name:"lin"};
var target = Object.assign({},source);
这样就可以把source对象里面的相同属性值拷贝到target对象
存在的问题:
如果要拷贝的对象source里面的属性不是基础类型也是对象,或者属性含有对象数组,这种方式的拷贝就不会把source对象里面的对象或者数组对象进行深拷贝
例:
var source = {"id":1,list:[{"name":"小明"},{"name":"小花"}]};
var target = Object.assign({},source);
这时target里面的属性list数组对象只是浅拷贝source里面的list,还是对同一块内存的引用
也可以向下面这样优化,循环list数组里面的对象进行Object.assign拷贝,添加到一个新的数组去,然后再赋值给target.list
例:
let source = {"id":1,list:[{"name":"小明"},{"name":"小花"}]};
let target = Object.assign({},source);
//对象数组的深拷贝
let newlist = [];
for(let i=0;i<target.list.length;i++){
let obj = Object.assign({}, target.list[i]);
newlist.push(obj);
}
target.list = newlist;
注:如果对象里面还含有对象属性,而里面的对象属性又含有对象属性,则这种方式很麻烦不合适。
三、循环递归深拷贝
function deepClone(obj, newObj) {
var newObj = newObj || {};
for (let key in obj) {
if (typeof obj[key] == 'object') {
let isArray = Array.isArray(obj[key]);//判断是否数组
newObj[key] = (isArray == true ? [] : {})
deepClone(obj[key], newObj[key]);
} else {
newObj[key] = obj[key]
}
}
return newObj;
}