JS中解决JSON中函数及对象的深度拷贝

在JS中对于普通的json, 可用如下方式进行简单的深度拷贝

1
2
let json = { a: "aa" };
let newJson = JSON.parse(JSON.stringify(json));

不过当json中包含一些JS中的对象及函数的时候, 用这样的方法会使数据丢失, 并且这个无法解决循环引用的问题, 所谓循环引用指的是

1
2
3
4
5
6
7
let b={};
let a={
b:b
};
b.a=a;
console.log(a);
// console.log(JSON.stringify(a));

这时JSON.stringify(a)就出现了异常

由于存在这些问题, 所以就编写了一个拷贝函数, 来做这件事情, 代码实现如下

实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function copyObject(o) {
let objectMap = new WeakMap();
function copy(object) {
if (objectMap.get(object)) {
return objectMap.get(object);
}
if (typeof object !== "object") {
return object;
}
if (object instanceof RegExp) {
return object;
}
let newObject = new object.constructor();
objectMap.set(object, newObject);
for (let k in object) {
newObject[k] = copy(object[k]);
}
return newObject;
}
return copy(o);
}

代码中通过let objectMap = new WeakMap();保存拷贝过的对象, 解决循环引用的问题

通过递归拷贝其中的对象, 若是基本类型、正则对象或函数则直接返回

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class ObjA {
constructor(v) {
this.a = v;
}
print() {
console.log(this.a || "!!!");
}
}
function ObjB(v) {
this.name
let a = v;
this.print = () => {
console.log(a || "!!!");
}
}
let objA = new ObjA(666);
let objB = new ObjB(777);
let json0 = {};
let json1 = {
a: () => 'aaa',
b: [123, "abc", /abcd/],
c: {
d: function () { return "ddd" },
e: [123, 2, 3],
f: objA,
g: objB
},
r: json0
}
json0.r = json1;

let json2 = copyObject(json1);
json2.c.e[1] = "asdasd";
json2.r.r.r.r.b[1] = "rrrr";
console.log(json1);
console.log(json2);

json2.c.f.print();
objA.a = 888;
objA.print();
json2.c.f.print();
json2.c.g.print();

经过测试, 以上场景的输出均与预计相同