深拷贝由浅入深

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    var obj = {
      a: {
        b: 1,
        c: 2
      },
      d: {
        e: 3,
        f: {
          g: 4,
          h: 45
        }
      }
    }
    // 为什么要用hasOwnproperty判断一层 深拷贝不需要赋值对象原型链上继承的属性
    // 所有继承了 Object 的对象都会继承到 hasOwnProperty 方法。这个方法可以用来检测一个对象是否含有特定的自身属性;和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。
    // 该方法还有许多问题 如:typeof 后面详解

    let deepClone = obj => {
      var target = {};
      for(var i in obj){
        if(obj.hasOwnProperty(i)){
          if(typeof obj[i] === 'object'){
            target[i] = deepClone(obj[i]);
          } else {
            target[i] = obj[i];
          }
        }
        
      }

      return target;
    }

    // 上述方法有很多问题问题
    /**
     * 没有对参数做校验  如果不是对象直接return
     * 判断对象的方法不严谨  Object.prototype.toString.call(obj, '[object Object]')
     * 没有考虑数组的兼容    
     * 当clone很深的话 爆栈  
     * 循环引用  var a = {}; a.a = a;  deepClone(a) 直接死循环   循环检测
    */

    // 创建一个对象
    function createData(deep, breadth) {
        var data = {};
        var temp = data;

        for (var i = 0; i < deep; i++) {
            temp = temp['data'] = {};
            for (var j = 0; j < breadth; j++) {
                temp[j] = j;
            }
        }

        return data;
    }

    // createData(1, 3); // 1层深度,每层有3个数据 {data: {0: 0, 1: 1, 2: 2}}
    // createData(3, 0); // 3层深度,每层有0个数据 {data: {data: {data: {}}}}

    // 破解递归爆栈
    /**
     * 第一种是消除尾递归
     * 循环  把对象看成一颗树 
    */

    // 用循环遍历一棵树,需要借助一个栈,当栈为空时就遍历完了,栈里面存储下一个需要拷贝的节点
    // 首先我们往栈里放入种子数据,key用来存储放哪一个父元素的那一个子元素拷贝对象
    // 然后遍历当前节点下的子元素,如果是对象就放到栈里,否则直接拷贝
    

    // let newObj = deepClone(createData(10000)); // 爆栈: Uncaught RangeError: Maximum call stack size exceeded
    // let newObj = deepClone(createData(10, 100000));  不会报错  广度不会爆栈



    // console.log(createData(10, 10));
    let deepCloneUpgrade = obj => {
      let target = {};
      let stack = [];
      if(Object.prototype.toString.call(obj) === '[object Object]'){
        stack.push({
            parent: target,
            key: undefined,
            data: obj
        });
        while(stack.length){
          console.log(stack)
          let item = stack.pop();
          const parent = item.parent;
          const key = item.key;
          const data = item.data;

          // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
          let res = parent;
          if(typeof key != 'undefined'){
            // 浅拷贝  parent[key] 和 res指向同一块内存地址 一直是 target[data] 、target[data][data]
            res = parent[key] = {};
          }

          for(let i in data){
            if(data.hasOwnProperty(i)){
              if(typeof data[i] === 'object'){
                stack.push({
                  parent: res,
                  key: i,
                  data: data[i]
                })
              }
              else {
                res[i] = data[i];
              }
            }
          }

          // console.log(target)
        }
      }

      return target;
    }

    // let newObj = deepCloneUpgrade(createData(2, 2)); 
    // console.log(newObj);

    var b = {};
    var a = {
      a1: b,
      a2: b
    }

    console.log(a.a1 === a.a2)


    // 循环深度深拷贝
    // 解决爆栈和循环引用的问题
    var deepClone = obj => {
      const uniqueList = [];
      const target = {};
      const stack = [];
      stack.push({
        parent: target,
        key: undefined,
        data: obj
      })

      while (stack.length) {
        let item = stack.pop();

        let parent = item.parent;
        let key = item.key;
        let data = item.data;

        let res = parent;
        if (typeof key != 'undefined') {
          res = parent[key] = {};
        }

        let uniqueData = find(uniqueList, data);
        if (uniqueData) {
          parent[key] = uniqueData.target;
          continue;
        }

        uniqueList.push({
          source: data,
          target: res
        })

        for (let i in data) {
          if (data.hasOwnProperty(i)) {
            if (typeof data[i] == 'object') {
              stack.push({
                parent: res,
                key: i,
                data: data[i]
              });
            } else {
              res[i] = data[i];
            }
          }
        }

      }

      return target;
    }
    function find(arr, item) {
      for (let i = 0; i < arr.length; i++) {
        if (arr[i].source === item) {
          return arr[i];
        }
      }

      return null;
    }

    var b = {};
    var a = { a1: b, a2: b };
    var c = deepClone(a);

    console.log(a.a1 === a.a2)
    console.log(c.a1 === c.a2)
    
  </script>
</body>
</html>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值