深拷贝与浅拷贝 
如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址,浅拷贝后的内容的内存地址相同
即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址
js
function shallowClone(obj) {
    const newObj = {};
    for(let prop in obj) {
        if(obj.hasOwnProperty(prop)){
            newObj[prop] = obj[prop];
        }
    }
    return newObj;
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
在 JavaScript 中,存在浅拷贝的现象有:
| 方法 | 示例 | 适用场景 | 
|---|---|---|
| Object.assign() | const copy = Object.assign({}, obj) | 简单对象合并,复制可枚举自有属性 | 
| 展开运算符 ... | const copy = { ...obj }const arrCopy = [...arr] | 对象或数组的快速浅拷贝 | 
| Array.prototype.slice() | const arrCopy = arr.slice() | 数组浅拷贝 | 
| Array.prototype.concat() | const arrCopy = [].concat(arr) | 数组浅拷贝 | 
| 手动遍历赋值 | javascript<br>const copy = {};<br>for (let key in obj) {<br> if (obj.hasOwnProperty(key)) {<br> copy[key] = obj[key];<br> }<br>} | 需要过滤原型链属性的场景 | 
深拷贝开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性
常见的深拷贝方式有:
| 方法 | 示例 | 优缺点 | 
|---|---|---|
| JSON.parse(JSON.stringify()) | const copy = JSON.parse(JSON.stringify(obj)) | 优点:简单 缺点:丢失函数、Symbol、undefined、循环引用报错 | 
| 递归实现 | 见下方完整代码 | 优点:可定制处理特殊类型 缺点:需处理复杂边界 | 
| _.cloneDeep(Lodash) | const copy = _.cloneDeep(obj) | 优点:功能完善 缺点:需引入第三方库 | 
| structuredClone(现代 API) | const copy = structuredClone(obj) | 优点:原生支持,处理循环引用 缺点:不兼容 IE,无法克隆函数、DOM | 
| jQuery.extend() | 
手写深拷贝
js
function deepClone(obj, hash = new WeakMap()) {
  if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  // 可能是对象或者普通的值  如果是函数的话是不需要深拷贝
  if (typeof obj !== "object") return obj;
  // 是对象的话就要进行深拷贝
  if (hash.get(obj)) return hash.get(obj);
  let cloneObj = new obj.constructor();
  // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
  hash.set(obj, cloneObj);
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      // 实现一个递归拷贝
      cloneObj[key] = deepClone(obj[key], hash);
    }
  }
  return cloneObj;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
js
function deepClone(target, map = new WeakMap()) {
  // 处理基本类型、函数(直接返回)
  if (typeof target !== 'object' || target === null) return target;
  // 处理循环引用:已拷贝过的对象直接返回
  if (map.has(target)) return map.get(target);
  // 处理特殊对象类型
  const constructor = target.constructor;
  let clone;
  switch (constructor) {
    case Date:
      clone = new Date(target.getTime());
      break;
    case RegExp:
      clone = new RegExp(target.source, target.flags);
      break;
    case Map:
      clone = new Map();
      target.forEach((v, k) => clone.set(deepClone(k, map), deepClone(v, map)));
      break;
    case Set:
      clone = new Set();
      target.forEach(v => clone.add(deepClone(v, map)));
      break;
    default:
      // 继承原型链
      clone = Object.create(Object.getPrototypeOf(target));
  }
  // 缓存当前对象,防止循环引用
  map.set(target, clone);
  // 处理普通对象和数组
  Reflect.ownKeys(target).forEach(key => {
    clone[key] = deepClone(target[key], map);
  });
  return clone;
}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
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
