深拷贝以及广度遍历深度优先

  1. 类型:

    1. 基本类型,

      1. 值引用:创建这个值的一个副本,并将该副本复制给新变量
      2. undefined,null,Boolean,String,Number,Symbol
      3. 基本类型值在内存中占据固定大小,保存在栈内存中
    2. 引用类型

      1. 地址引用:复制的是指针,最终两个变量最终都指向同一个对象
      2. Object,Array,Date,Function,RegExp等
      3. 引用类型的值是对象,保存在堆内存中,而栈内存存储的是对象的变量标识符以及对象在堆内存中的存储地址
      // 值引
      
      
用
      function fn(a) {
      	a = 2
      	console.log('inside',a)
      }
      var a = 1
      fn(a)
      console.log('outside',a)
      
      // 地址引用
      function fn(obj) {
      	obj.a = 2
      	console.log('inside',obj)
      }
      var obj = {a: 1}
      fn(obj)
      console.log('outside',obj)
      
      `
  复杂数据类型的数据复制的是地址,修改会被“共享”

  怎么函数里面修改对象但是不修改传进来的值?

  ```
  function fn(obj) {
  	// obj.a = 2
  	obj = Object.assign({}, obj, {a: 2})
  	console.log('inside',obj)
  }
  var obj = {a: 1}
  fn(obj)
  console.log('outside',obj)
  
  ```

  如果上面写成 obj = Object.assign(obj, {a: 2}),那么结果和例子1一样。为什么?
  Object.assign()会递归合并所有对象的属性到第一个参数里面,使用空对象便是新的对象,
  如果省了`{}`,那么就会合并对原来的`obj`上,变成地址引用了
  es6写法:`obj = {...obj, a: 2}`, 后面新增的属性是直接写,不需要大括号。
  1. 浅拷贝:复制了引用,彼此之间的操作会互相影响

    1. splice(会改变原始数组
    2. concat(不会改变现有的数组
  2. 深拷贝:在堆中重新分配内存,不同的地址,相同的值,互不影响

    实现方式有以下几种

    ​ 先定义测试数据

    // 模拟循环引用
      let obj1 = {
          e: 2,
          a:{
              b:44
          }
      }
      obj1.o = {
          m:obj1
      };
      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;
              }
          }
          console.log(data)
          return data;
        }
    
    1. parse,stringify

      ​ 有以下缺点

      1. 会忽略 undefined

      2. 会忽略 Symbol

      3. 无法序列化function,也会忽略

      4. 无法解决循环引用,会报错

      5. 深层对象转换爆栈

      6. 实例

        function clone(obj){
        	return JSON.parse(JSON.stringify(obj))
        }
        var oldObj = {
          name: "old",
          age: undefined,
          sex: Symbol("setter"),
          title: function() {},
          lastName: null
        };
        var newObj = clone(oldObj);
        // 可以看到会忽略undefined、symbol、function的对象
        console.log(newObj); // {name: "old", lastName: null}
        
        var firstObj = {
          name: "firstObj"
        };
        firstObj.newKey = firstObj;
        // Converting circular structure to JSON
        var newFirstObj = clone(firstObj);
        
        // clone(createData(1000)) // ok
        // clone(createData(10000)) // 爆栈
        
    2. jq

      1. $.extend(true,[],object) // 第一个参数设置为true是深拷贝
    3. 深拷贝,递归复制,爆栈

      function getType(obj){
           //tostring会返回对应不同的标签的构造函数
           var toString = Object.prototype.toString;
           var map = {
           '[object Boolean]'  : 'boolean', 
           '[object Number]'   : 'number', 
           '[object String]'   : 'string', 
           '[object Function]' : 'function', 
           '[object Array]'    : 'array', 
           '[object Date]'     : 'date', 
           '[object RegExp]'   : 'regExp', 
           '[object Undefined]': 'undefined',
           '[object Null]'     : 'null', 
           '[object Object]'   : 'object'
           };
           if(obj instanceof Element) { // Element是元素对象
      	     return 'element';
           }
           return map[toString.call(obj)];
      }
      function deepClone(data){
          var type = getType(data);
          var obj;
          if(type === 'array'){
          	obj = [];
          } else if(type === 'object'){
          	obj = {};
          } else {
              //不再具有下一层次
              return data;
          }
          if(type === 'array'){
              for(var i = 0, len = data.length; i < len; i++){
                  obj.push(deepClone(data[i]));
              }
          } else if(type === 'object'){
              for(var key in data){
              	obj[key] = deepClone(data[key]);
              }
          }
          return obj;
      }
      deepClone(1000) // ok
      deepClone(10000) // 爆栈
      
    4. Object.create()的实现(不会爆栈

      function deepClone2(obj) {
        var copy = Object.create(Object.getPrototypeOf(obj));
        var propNames = Object.getOwnPropertyNames(obj);
        
        propNames.forEach(function(name) {
          var desc = Object.getOwnPropertyDescriptor(obj, name);
          Object.defineProperty(copy, name, desc);
        });
        
        return copy;
      }
      // deepClone2(createData(1000)) // ok
      // deepClone2(createData(10000)) // ok 再继续加都ok,直到浏览器奔溃
      
    5. 广度遍历

      function cloneLoop(x) {
          const root = {};
          // 栈
          const loopList = [
              {
                  parent: root,
                  key: undefined,
                  data: x, // 先赋值给data
              }
          ];
          while(loopList.length) {
          	  // 深度优先
              // 弹出给node
              const node = loopList.pop();
              const parent = node.parent;
              const key = node.key; // 当前对象的key值,比如以下的a,b,c,d
              const data = node.data;
       		
              // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
              let res = parent; // 相当于赋值给root,parant和res是临时变量,最后是输出root
              if (typeof key !== 'undefined') { // 不是对象的都直接复制
                  res = parent[key] = {}; // 将res的指针指向parent[key],即当前要查询的下标值。
              }
       
              for(let k in data) { // 子对象遍历
                  if (data.hasOwnProperty(k)) { // 当前子对象属性是否还有下一层
                      if (typeof data[k] === 'object') {
                          // 下一次循环
                          loopList.push({
                              parent: res, // 将上面的res收集到的属性累加起来。
                              key: k, // 下一个要遍历的key
                              data: data[k], // 下一个要遍历的子对象
                          });
                      } else {
                          res[k] = data[k]; 
                          // 当前子对象属性没有下一层对象,直接赋值, 
                          // 存值其实是存在parent[key][k] ,同步到root里面
                      }
                  }
              }
          }
       
          return root;
        }
        // cloneLoop(createData(100000)) // 不会爆
        // cloneLoop(oldObj)
        
        let obj3 = {
          a:1,
          b: {
            c: 2,
            d: {
              e:3
            },
          }
        }
        cloneLoop(obj3)
        
      
原文地址:https://www.cnblogs.com/huangjunjia/p/13068164.html