用循环的方式代替递归进行深拷贝(简版)

以下为待拷贝对象:

1
2
3
4
5
6
7
8
9
let a = {
a1: 1,
a2: {
b1: 1,
b2: {
c1: 1
}
}
}

可以转换成树形结构:

1
2
3
4
5
6
7
8
9
   a
/ \
a1 a2
| / \
1 b1 b2
| |
1 c1
|
1

用循环遍历一棵树,需要借助一个栈,当栈为空时就遍历完了,栈里面存储下一个需要拷贝的节点

首先往栈里放入种子数据,key 用来存储放哪一个父元素的哪一个子元素拷贝对象

然后遍历当前节点下的子元素,如果是对象就放到栈里,否则直接拷贝。

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
function cloneLoop(x) {
const root = {} // 根节点
// 处理的栈
const cloneStack = [
{
parent: root,
key: undefined,
data: x
}
]

while (cloneStack.length) {
// 深度优先
const node = cloneStack.pop()
const parent = node.parent
const key = node.key
const data = node.data

// 初始化复制目标,囡key为undefined则直接拷贝到父元素,否则到子元素
let res = parent
if (key !== undefined) {
res = parent[key] = {} // res同parent[key],以下处理res
}

for (const key in data) {
if (data.hasOwnProperty(key)) {
if (isObject(data[key])) {
// 如果子元素存在对象,则往栈顶推入一个待处理的对象
cloneStack.push({
parent: res,
key: key,
data: data[key]
})
} else {
res[key] = data[key] // 基础类型直接复制
}
}
}
}

return root
}

// 判断是否为对象
function isObject(x) {
if (typeof x === 'object' || typeof x === 'function') {
if (x.nodeType === 1) {
return false // 排除html元素
} else {
return Object.prototype.toString.call(x) === '[object Object]'
}
} else {
return false
}
}

// test demo

let a = {
a1: 1,
a2: {
b1: 1,
b2: {
c1: 1
}
}
}

let b = cloneLoop(a)

a.a2.b1 = 4

console.log(a) // { a1: 1, a2: { b1: 4, b2: { c1: 1 } } }
console.log(b) // { a1: 1, a2: { b1: 1, b2: { c1: 1 } } }