先上代码
<!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'] = 2newobj['d']['e'] = 5newobj['f'][0] = 9console.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分支进行递归。
如最后注释中的结果一样,改变新对象的属性值对旧对象不造成影响,反之亦然,两个对象互相独立。如此便完成了对对象的深拷贝。