> For the complete documentation index, see [llms.txt](https://shenjunhong.gitbook.io/blog/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://shenjunhong.gitbook.io/blog/yuan-ma/yuan-ma-shou-xie-xi-lie/shen-qian-kao-bei.md).

# 深浅拷贝 深拷贝

复制对象没有保持和原对象一样的结构。因此，JSON 实现深复制不能处理指向相同引用的情况，相同的引用会被重复复制。

* JSON.parse(JSON.stringify(data))

如果对象中存在循环引用的情况也无法正确实现深拷贝

如果 data 里面有时间对象， 则 JSON.stringify 后再 JSON.parse 的结果， 时间将只是字符串的形式，而不是时间对象

如果 data 里有函数, 则序列的结果会把函数置为 undefined

无法拷贝 copyObj 对象原型链上的属性和方法；

取不到值为 undefined 的 key；

如果对象里有函数，函数无法被拷贝下来

递归实现 JS 原生的方法不能很好的实现深复制，那么我们就动手实现一个。

思想非常简单：对于简单类型，直接复制。对于引用类型，递归复制它的每一个属性。

我们需要解决的问题：

* 循环引用
* 相同引用

### 解决循环引用问题

考虑 WeakMap

但是 WeakMap 比 Map 有两个不同：

1、【特殊点】WeakMap 只接受引用类型（对象）作为键名； 2、【优点】WeakMap 的键名所指向的对象都是弱引用，不计入垃圾回收机制，不用考虑内存泄漏。 当引用对象被清除，其所对应的 WeakMap 记录就会自动被移除。

## 实现 一个浅克隆

```js
const shallClone = (target) => {
  if (typeof target === "object" && target !== null) {
    const cloneTarget = Array.isArray(target) ? [] : {};
    for (let prop in target) {
      if (target.hasOwnProperty(prop)) {
        // 遍历对象自身可枚举属性（不考虑继承属性和原型对象）
        cloneTarget[prop] = target[prop];
      }
    }
    return cloneTarget;
  } else {
    return target;
  }
};
```

## 实现一个 深克隆

```js
/**
 * 深克隆（深拷贝）+ 解决深拷贝函数中循环引用时导致的栈溢出的问题
 * @param {object} origin
 * @param {*} hashMap WeakMap数据，用于缓存克隆过的对象
 * @returns origin / 克隆的origin
 */
function deepCloneCycle(origin, hashMap = new WeakMap()) {
  let result = null;
  if (hashMap.has(origin)) return hashMap.get(origin); // 查缓存字典中是否已有需要克隆的对象，有的话直接返回同一个对象（同一个引用，不用递归无限创建进而导致栈溢出了）;
  // typeof null === 'object' true
  // typeof [] === 'object' true
  // typeof NaN === 'object' false
  if (typeof origin === "object" && origin !== null) {
    // 【类型判断】引用类型，进行递归拷贝（用typeof判断类型要剔除null的情况）
    if (Object.prototype.toString.call(origin) === "[object Array]") {
      // 【类型判断】数组类型，创建一个新数组
      result = [];
      hashMap.set(origin, result); // 哈希表缓存新值
      // 【遍历赋值】
      origin.forEach((el) => {
        result.push(deepCloneCycle(el, hashMap)); // 【递归】
      });
    } else if (Object.prototype.toString.call(origin) === "[object Object]") {
      // 【类型判断】对象类型，创建一个新对象
      result = {};
      hashMap.set(origin, result); // 哈希表缓存新值
      for (const key in origin) {
        // 【遍历赋值】对象这里特殊处理了，不遍历拷贝原型链上的属性
        if (origin.hasOwnProperty(key)) {
          result[key] = deepCloneCycle(origin[key], hashMap); // 【递归】
        }
      }
    }
  } else {
    // 【类型判断】原始类型直接返回
    return origin;
  }
  return result;
}
```
