简单实现
其实深拷贝可以分为两步,浅拷贝+递归,如果遇到对象就进行递归操作,两者相结合就实现了深拷贝。
1 | function cloneShallow(source) { |
2 | var target = Array.isArray(source) ? [] : {}; |
3 | for (var key in source) { |
4 | if (Object.prototype.hasOwnProperty.call(source, key)) { |
5 | if (isObject(source[key])) { |
6 | target[key] = cloneDeep1(source[key]); // 注意这里 |
7 | } else { |
8 | target[key] = source[key]; |
9 | } |
10 | } |
11 | } |
12 | return target; |
13 | } |
14 | |
15 | // 测试用例 |
16 | var a = { |
17 | name: "muyiy", |
18 | book: { |
19 | title: "You Don't Know JS", |
20 | price: "45" |
21 | }, |
22 | a1: undefined, |
23 | a2: null, |
24 | a3: 123 |
25 | } |
26 | var b = cloneShallow(a); |
27 | |
28 | a.name = "高级前端进阶"; |
29 | a.book.price = "55"; |
30 | |
31 | console.log(b); |
32 | // { |
33 | // name: 'muyiy', |
34 | // book: { title: 'You Don\'t Know JS', price: '55' }, |
35 | // a1: undefined, |
36 | // a2: null, |
37 | // a3: 123 |
38 | // } |
处理循环引用
只需要使用一个hash来保存已经处理过的对象即可,这里使用ES6的WeakMap
1 | function cloneDeep2(source,hash = new WeakMap()){ |
2 | if(!isObject(source)) return source; |
3 | if(hash.has(source)) return hash.get(source) |
4 | |
5 | var target = Array.isArray(source) ? [] : {}; |
6 | hash.set(source, target); // 新增代码,哈希表设值 |
7 | |
8 | for(var key in source) { |
9 | if (Object.prototype.hasOwnProperty.call(source, key)) { |
10 | if (isObject(source[key])) { |
11 | target[key] = cloneDeep2(source[key], hash); // 新增代码,传入哈希表 |
12 | } else { |
13 | target[key] = source[key]; |
14 | } |
15 | } |
16 | } |
17 | return target; |
18 | } |
在ES5中没有WeakMap,使用数组代替
1 | function cloneDeep3(source, uniqueList=[]) { |
2 | |
3 | if (!isObject(source)) return source; |
4 | |
5 | var target = Array.isArray(source) ? [] : {}; |
6 | |
7 | // ============= 新增代码 |
8 | // 数据已经存在,返回保存的数据 |
9 | var uniqueData = find(uniqueList, source); |
10 | if (uniqueData) { |
11 | return uniqueData.target; |
12 | }; |
13 | |
14 | // 数据不存在,保存源数据,以及对应的引用 |
15 | uniqueList.push({ |
16 | source: source, |
17 | target: target |
18 | }); |
19 | // ============= |
20 | |
21 | for(var key in source) { |
22 | if (Object.prototype.hasOwnProperty.call(source, key)) { |
23 | if (isObject(source[key])) { |
24 | target[key] = cloneDeep3(source[key], uniqueList); // 新增代码,传入数组 |
25 | } else { |
26 | target[key] = source[key]; |
27 | } |
28 | } |
29 | } |
30 | return target; |
31 | } |
32 | |
33 | // 新增方法,用于查找 |
34 | function find(arr, item) { |
35 | for(var i = 0; i < arr.length; i++) { |
36 | if (arr[i].source === item) { |
37 | return arr[i]; |
38 | } |
39 | } |
40 | return null; |
41 | } |
处理递归暴栈
使用递归的方式处理深拷贝,如果对象过大可能会导致暴栈的问题,这里把递归变成循环,使用一个列表
1 | function deepClone4(source) |
2 | { |
3 | const hash = new WeakMap() |
4 | |
5 | var target = Array.isArray(source) ? [] : {}; |
6 | const loopList = [ |
7 | { |
8 | parent: target, |
9 | data: source, |
10 | key: undefined |
11 | } |
12 | ] |
13 | |
14 | while (loopList.length) { |
15 | const node = loopList.pop() |
16 | const { parent, data, key } = node |
17 | |
18 | if (typeof data === 'object') { |
19 | let child = Array.isArray(data) ? [] : {}; |
20 | if (hash.get(data)) { |
21 | child = hash.get(data) |
22 | parent[key] = child |
23 | } else { |
24 | if (!key) { |
25 | child = parent |
26 | } else { |
27 | parent[key] = child |
28 | hash.set(data, child) |
29 | } |
30 | for (let k in data) { |
31 | loopList.push({ |
32 | parent: child, |
33 | key: k, |
34 | data: data[k] |
35 | }) |
36 | } |
37 | |
38 | } |
39 | } else { |
40 | parent[key] = data |
41 | } |
42 | } |
43 | |
44 | return target; |
45 | } |