Home 深拷贝
Post
Cancel

深拷贝

1. 需要考虑的问题

  • 基本实现
    • 递归能力
  • 循环引用
    • 考虑问题的全面性
    • 理解 weakmap 的真正意义
  • 多种类型
    • 考虑问题的严谨性
    • 创建各种引用类型的方法,JS API 的熟练程度
    • 准确的判断数据类型,对数据类型的理解程度
  • 通用遍历:
    • 写代码可以考虑性能优化
    • 了解集中遍历的效率
    • 代码抽象能力
  • 拷贝函数:
    • 箭头函数和普通函数的区别
    • 正则表达式熟练程度

2. 实现

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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/*
 * @Author: Dalegac
 * @Date: 2021-09-30 14:28:05
 * @LastEditTime: 2021-10-04 10:11:43
 * @LastEditors: Dalegac
 * @Description: Just say something
 */
// 可继续遍历的数据类型
const mapTag = "[object Map]";
const setTag = "[object Set]";
const arrayTag = "[object Array]";
const objectTag = "[object Object]";
const argsTag = "[object Arguments]";

// 不可继续遍历的数据类型
const boolTag = "[object Boolean]";
const dateTag = "[object Date]";
const numberTag = "[object Number]";
const stringTag = "[object String]";
const symbolTag = "[object Symbol]";
const bigintTag = "[object BigInt]";
const errorTag = "[object Error]";
const regexpTag = "[object RegExp]";
const funcTag = "[object Function]";

const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag];

/**
 * @description: 工具函数 - 通用while循环
 * @param {Array}array 待遍历数组
 * @param {Function}iteratee 执行函数
 * @return {Array}
 */
function forEach(array, iteratee) {
  let index = -1;
  const length = array.length;
  while (++index < length) {
    iteratee(array[index], index);
  }
  return array;
}
/**
 * @description: 工具函数 - 判断是否为引用类型
 * @param {Any} target
 * @return {Boolean} 是否是引用类型
 */
function isObject(target) {
  const type = typeof target;
  return target !== null && (type === "object" || type === "function");
}
/**
 * @description: 工具函数 - 获取实际类型
 * @param {Any} target
 * @return {String} 格式化类型
 */
function getType(target) {
  return Object.prototype.toString.call(target);
}

/**
 * @description:工具函数 - 初始化被克隆函数
 * @param {Any} target
 * @return {*}
 */
function getInit(target) {
  const Ctor = target.constructor;
  return new Ctor();
}
/**
 * @description: 工具函数 - 克隆Symbol
 * @param {Symbol}
 * @return {*}
 */
function cloneSymbol(target) {
  return Object(Symbol.prototype.valueOf.call(target));
}

/**
 * @description: 工具函数 - 克隆正则
 * @param {Regexp} target
 * @return {*}
 */
function cloneReg(target) {
  const { source, flags, lastIndex } = target;
  const result = new RegExp(source, flags);
  result.lastIndex = lastIndex;
  return result;
}

/**
 * @description: 工具函数 - 克隆函数
 * @param {Function} target
 * @return {*}
 */
function cloneFunction(target) {
  const bodyReg = /(?<={).+(?=})/ms;
  const paramReg = /(?<=\().+(?=\)\s+{)/;
  const funcString = target.toString();
  if (target.prototype) {
    const param = paramReg.exec(funcString);
    const body = bodyReg.exec(funcString);
    if (body) {
      if (param) {
        const paramArr = param[0].split(",");
        return new Function(...paramArr, body[0]);
      } else {
        return new Function(body[0]);
      }
    } else {
      return null;
    }
  } else {
    return eval(funcString);
  }
}

/**
 * @description: 工具函数 - 克隆不可遍历类型
 * @param {Any} target 被克隆对象
 * @param {String} type 对象类型
 * @return {*}
 */
function cloneOtherType(target, type) {
  const Ctor = target.constructor;
  switch (type) {
    case boolTag:
    case numberTag:
    case stringTag:
    case bigintTag:
    case errorTag:
    case dateTag:
      return new Ctor(target);
    case regexpTag:
      return cloneReg(target);
    case symbolTag:
      return cloneSymbol(target);
    case funcTag:
      return cloneFunction(target);
    default:
      return null;
  }
}

/**
 * @description: 克隆函数
 * @param {Any} target 被克隆对象
 * @param {WeakMap} map 引用对象缓存池
 * @return {*}
 */
function clone(target, map = new WeakMap()) {
  // 克隆原始类型
  if (!isObject(target)) {
    return target;
  }
  // 初始化
  const type = getType(target);
  let cloneTarget;
  if (deepTag.includes(type)) {
    cloneTarget = getInit(target);
  } else {
    return cloneOtherType(target, type);
  }

  // 防止循环引用
  if (map.get(target)) {
    return map.get(target);
  }
  map.set(target, cloneTarget);

  // 克隆set
  if (type === setTag) {
    target.forEach((value) => {
      cloneTarget.add(clone(value, map));
    });
    return cloneTarget;
  }

  // 克隆map
  if (type === mapTag) {
    target.forEach((value, key) => {
      cloneTarget.set(key, clone(value, map));
    });
    return cloneTarget;
  }

  // 克隆对象和数组
  const keys = type === arrayTag ? undefined : Object.keys(target);
  forEach(keys || target, (value, key) => {
    if (keys) {
      key = value;
    }
    cloneTarget[key] = clone(target[key], map);
  });
  return cloneTarget;
}
module.exports = { clone };

3. 参考

This post is licensed under CC BY 4.0 by the author.