手写 deepClone

2022/8/12 javascript
function deepClone(obj, hash = new WeakMap()) {
  if (hash.has(obj)) {
    return obj;
  }
  let res = null;
  const reference = [Date, RegExp, Set, WeakSet, Map, WeakMap, Error];

  if (reference.includes(obj?.constructor)) {
    res = new obj.constructor(obj);
  } else if (Array.isArray(obj)) {
    res = [];
    obj.forEach((e, i) => {
      res[i] = deepClone(e);
    });
  } else if (typeof obj === "object" && obj !== null) {
    res = {};
    for (const key in obj) {
      if (Object.hasOwnProperty.call(obj, key)) {
        res[key] = deepClone(obj[key]);
      }
    }
    hash.set(obj, res);
  } else {
    res = obj;
  }
  return res;
}

/**
 * @param {object} obj 需要拷贝的对象
 * @returns {object} 拷贝后的对象
 */
function deepClone(obj) {
  return Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
}

/**
 * @param {object} origin 源对象
 * @return {object} 拷贝后的对象
 */
// 利用 WeakMap 解决对象相互引用的问题
function deepClone(origin, hashMap = new WeakMap()) {
  if (origin == undefined || typeof origin !== "object") return origin;

  if (origin instanceof Date) return new Date(origin);
  if (origin instanceof RegExp) return new RegExp(origin);

  // 若之前已经处理过当前源对象,则直接返回 WeakMap 中保存的目标对象
  const hashValue = hashMap.get(origin);
  if (hashValue) return hashValue;

  // 利用源对象的构造器创建目标对象
  const target = new origin.constructor();
  // 将 {源对象: 目标对象} 保存到 WeakMap 中
  hashMap.set(origin, target);

  for (let key in origin) {
    if (origin.hasOwnProperty(key))
      target[key] = deepClone(origin[key], hashMap);
  }

  return target;
}

const obj = {
  a: 1,
  b: 2,
  c: {
    d: 3,
    e: 4,
  },
};

const newObj = deepClone(obj);
newObj.c.d = 4;

console.log(obj);
console.log(newObj);

let obj1 = {};
let obj2 = {};
obj2.obj1 = obj1;
obj1.obj2 = obj2;

console.log(deepClone(obj2));

function deepClone(target) {
  const map = new WeakMap();

  function isObject(target) {
    return (
      (typeof target === "object" && target) || typeof target === "function"
    );
  }

  function clone(data) {
    if (!isObject(data)) {
      return data;
    }
    if (typeof data === "symbol") {
      return Symbol.for(data.description);
    }
    if ([Date, RegExp].includes(data.constructor)) {
      return new data.constructor(data);
    }
    if (typeof data === "function") {
      return new Function("return " + data.toString())();
    }
    const exist = map.get(data);
    if (exist) {
      return exist;
    }
    if (data instanceof Map) {
      const result = new Map();
      map.set(data, result);
      data.forEach((val, key) => {
        if (isObject(val)) {
          result.set(key, clone(val));
        } else {
          result.set(key, val);
        }
      });
      return result;
    }
    if (data instanceof Set) {
      const result = new Set();
      map.set(data, result);
      data.forEach((val) => {
        if (isObject(val)) {
          result.add(clone(val));
        } else {
          result.add(val);
        }
      });
      return result;
    }
    const keys = Reflect.ownKeys(data);
    const allDesc = Object.getOwnPropertyDescriptors(data);
    const result = Object.create(Object.getPrototypeOf(data), allDesc);
    map.set(data, result);
    keys.forEach((key) => {
      const val = data[key];
      if (isObject(val)) {
        result[key] = clone(val);
      } else {
        result[key] = val;
      }
    });
    return result;
  }

  return clone(target);
}
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
Last Updated: 2022/9/18 下午9:15:31