深拷贝和浅拷贝
- 浅拷贝:只是复制对象的
第一级属性值,如果对象的第一级属性中包含引用类型,则只复制地址。 - 深拷贝:不但复制对象的第一级属性值,即使对象中又包含
引用类型的属性值,也会继续复制内嵌类型的属性值。形成两个毫无瓜葛的对象
浅拷贝
Object.assign(target, source) - 浅拷贝
var obj1 = {
x: 1,
a: { b: 4 },
c: undefined,
f: function() { console.info("f") }
}
var obj2 = Object.assign({}, obj1)
obj1 == obj2 // false;
var obj3 = obj1;
obj1 == obj3; // true
展开运算符...
和Object.assign()的功能相同
var obj1 = {
x: 1,
a: { b: 4 },
c: undefined,
f: function() { console.info("f") }
}
var obj2 = {...obj1}
Array.concat()
Array.slice()
深拷贝
JSON.stringify & JSON.parse - 深拷贝
var obj1 = {
x: 1,
a: { b: 4 },
c: undefined, // 会丢失
f: function() { console.info("f") } // 会丢失
}
var str = JSON.stringify(obj1);
var obj2 = JSON.parse(str)
缺点:
- 无法拷贝
undefined;上面的例子中,obj2没有c属性 - 无法拷贝
内嵌函数;上面例子中,obj2中没有f属性
递归方法(重要)
基础版本
function cloneDeep(target) {
var newObj;
if (typeof target === "object") {
if (Array.isArray(target)) {
newObj = []
} else {
newObj = {}
}
for (const key in target) {
// 递归
newObj[key] = deepClone(target[key]);
}
} else {
newObj = target;
}
return newObj;
}
测试
var target = {
field1: 1,
field2: undefined,
field3: {
child: 'child'
},
field4: [2, 4, 8]
};
var res = cloneDeep(target);
console.info(res);
似乎没问题,但是如果遇到循环引用时,会报内存溢出的错误
target.target = target;
优化
思路:使用一个Map,用于保存已经遍历过的,如果已经存在过,直接返回
function cloneDeep(target, map = new Map()) {
var newObj;
if (typeof target === "object") {
newObj = Array.isArray(target) ? [] : {};
if (map.get(target)) {
return map.get(target)
}
map.set(target, newObj)
for (var key in target) {
newObj[key] = cloneDeep(target[key], map);
}
} else {
newObj = target
}
return newObj;
}
var target = {
field1: 1,
field2: undefined,
field3: {
child: 'child'
},
field4: [2, 4, 8]
};
target.target = target;
var res = cloneDeep(target);
console.info(res);