先上代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
obj = {
a : 1,
b : 2,
c : 3,
d : {
e : 4
},
f : [1,2,3]
}
function deepcopy(obj) {
// 如果obj是数组,返回数组的深拷贝
if (Array.isArray(obj)){
// 巧妙利用slice()方法完成深拷贝
return obj.slice()
// 利用toString()方法判断obj是否为对象
}else if (obj.toString() === '[object Object]'){
// 将对象转化为二维数组
var arr = Object.entries(obj)
var newobj = {}
for (var i = 0; i < arr.length; i++){
if(typeof arr[i][1] != 'object'){
// 如果属性类型不是object,将属性添加到新对象中
// 这里为什么不用toString()方法,很重要,下面会讲
newobj[arr[i][0]] = arr[i][1]
} else {
// 否则再次调用deepcopy函数(递归)
newobj[arr[i][0]] = deepcopy(arr[i][1])
}
}
// 返回新对象
return newobj
} else {
// obj不是对象时返回函数或其它原始数据类型
return obj
}
}
newobj = deepcopy(obj)
// console.log(newobj);
newobj['c'] = 2
newobj['d']['e'] = 5
newobj['f'][0] = 9
console.log(newobj);
// newobj = {
// a : 1,
// b : 2,
// c : 2,
// d : {
// e : 5
// },
// f : [9,2,3]
// }
console.log(obj);
// obj = {
// a : 1,
// b : 2,
// c : 3,
// d : {
// e : 4
// },
// f : [1,2,3]
// }
</script>
</body>
</html>
为什么31行不再使用toString()方法判断属性是否为'[object, Object]',而使用typeof判断,是因为如果原对象中有属性值为数组的,如f : [1, 2, 3],需要使它进行递归使其进行深拷贝,因为数组是引用数据类型,如果不深拷贝,改变原对象里数组的值,新对象也会改变,这样就不算对象的深拷贝了。而且typeof 数组 和 typeof 对象 的值都是'object',这样判断能使数组和对象都进入else分支进行递归。
如最后注释中的结果一样,改变新对象的属性值对旧对象不造成影响,反之亦然,两个对象互相独立。如此便完成了对对象的深拷贝。