zoukankan      html  css  js  c++  java
  • 559 对象和数组的深浅拷贝

    数组浅拷贝练习题

    let utils = (function () {
      /*
       * toArray:转换为数组的方法
       *   @params
       *      不固定数量,不固定类型
       *   @return
       *      [Array] 返回的处理后的新数组
       * by haha on 2020
       */
    
      // 方法1
      function toArray(...args) {
        // ES6的剩余运算符获取的参数集合本身就是数组
        return args;
      }
    
      // 方法2
      function toArray() {
        // arguments是实参集合,获取的结果是一个类数组(箭头函数中没有arguments),不能直接调取数组的办法(因为它不是ARRAY的实例)
        // return [...arguments];
        return Array.from(arguments);
      }
    
      // 方法3
      function toArray() {
        // 把类数组转换为数组(克隆)
        // 原理:只要把slice执行,让方法中的this变为arguments,这样就可以实现把类数组arguments转换为数组
        // 1.让slice执行: Array.prototype.slice()  或者  [].slice()
        // 2.把this改变成为arguments   [].slice.call(arguments)
        // 前提:操作数组的代码(内置方法中的代码)也需要适配改变的THIS(类数组)
    
        [].forEach.call(arguments, item => {
          console.log(item);
        });
    
        return [].slice.call(arguments);
    
        /* let arr = [];
        for (let i = 0; i < arguments.length; i++) {
          arr.push(arguments[i]);
        }
        return arr; */
      }
    
      return {
        toArray
      };
    })();
    
    let ary = utils.toArray(10, 20, 30); // => [10,20,30]
    console.log(ary);
    
    ary = utils.toArray('A', 10, 20, 30); // => ['A',10,20,30]
    console.log(ary);
    
    
    // 简单模拟slice的实现
    Array.prototype.slice = function slice() {
      // this -> ary
      let arr = [];
      for (let i = 0; i < this.length; i++) {
        arr.push(this[i]);
      }
      return arr;
    };
    // let ary = [10, 20, 30, 40];
    // ary.slice() 把数组克隆一份
    // console.log(ary.slice());
    

    浅拷贝

    • 克隆:内存地址是不一样的

    【for循环也可以实现浅拷贝。】

    实现对象的克隆:

    Object.assign:浅比较/浅克隆

    {...obj}:展开运算符,也只能展开第一级,也是浅克隆

    newObj = obj :这不是克隆,只是赋值

    let obj1 = { a: 1, b: 2, c: [5, 6] }
    let obj2 = Object.assign({}, obj1)
    console.log(obj2, obj1 == obj2) // {a: 1, b: 2, c: Array(2)}   false
    console.log(obj1.c === obj2.c) // true
    
    let obj3 = { ...obj1 }
    console.log(obj3) // {a: 1, b: 2, c: Array(2)}
    console.log(obj1.c === obj3.c) // true
    

    实现数组的克隆

    slice、concat... 这些都是浅克隆

    forEach、map、reduce... 遍历也只是遍历第一级

    let newArr = arr.concat([]);

    console.log(newArr === arr); // => false

    console.log(newArr[2] === arr[2]); // => true

    // 补充:使用for循环实现对象、数组的浅拷贝
    // 使用for循环实现对象的浅拷贝
    let obj1 = { a: 1, b: 2, c: [5, 6] }
    let newObj = {}
    
    for (let i in obj1) {
        newObj[i] = obj1[i]
    }
    
    console.log(newObj) // {a: 1, b: 2, c: Array(2)}
    console.log(obj1.c === newObj.c) // true
    
    
    // ---------------------------
    
    
    // 使用for循环实现数组的浅拷贝
    let arr = [11, 22, {name: '哈哈'}]
    let newArr = []
    
    for(let i=0;i<arr.length;i++) {
        newArr[i] = arr[i]
    }
    console.log(newArr) // [11, 22, {name: '哈哈'}]
    console.log(newArr[2] === arr[2]) // true
    

    深拷贝

    JSON.parse(JSON.stringify())

    // 暴力法:把原始数据直接变为字符串,再把字符串变为对象(此时浏览器要重新开辟所有的内存空间),实现出深度克隆、深拷贝
    let arr = [11, 22, { name: '哈哈' }]
    let newArr = JSON.parse(JSON.stringify(arr));
    console.log(newArr);
    console.log(newArr === arr); // => false
    console.log(newArr[2] === arr[2]); // => false
    
    // 出现的问题:正则会变为空对象,日期变为字符串,函数、undefined、Symbol直接消失,BigInt直接报错
    let newObj = JSON.parse(JSON.stringify(obj));
    console.log(newObj);
    console.log(newObj === obj); // => false
    console.log(newObj.c === obj.c); // => false
    

    自己实现深拷贝 【有缺陷,不用】

    /* 
    补充:获取实例的构造函数
    console.log((11).constructor) // Number函数
    console.log(('11').constructor) // String函数
    console.log((true).constructor) // Boolean函数
    console.log((false).constructor) // Boolean函数
    console.log((BigInt(10)).constructor) // BigInt函数
    console.log((BigInt('33')).constructor) // BigInt函数
    // console.log((undefined).constructor) // 报错
    // console.log((null).constructor) // 报错
    console.log((function fn() {}).constructor) // Function函数
    console.log(({}).constructor) // Object函数
    */
    
    
    /* 数组或者对象的深克隆/浅克隆 */
    let obj = {
      a: 100,
      b: [10, 20, 30],
      c: function () { },
      d: /^d+$/,
      e: new Date(),
      f: Symbol('f'),
      g: BigInt('10')
    };
    
    let arr = [10, [100, 200], {
      x: 10,
      y: 20
    }];
    
    // 深克隆、深拷贝
    function cloneDeep(obj) {
      // 获取实例的构造函数 【当obj是部分基本数据类型,或者是空对象{}的时候,这样写有问题,应该先用Object(obj)转换】
      const constructor = obj.constructor;
    
      if (obj === null) return null;
      if (typeof obj !== "object") return obj;
      // 正则实例、日期实例的constructor都是返回RegExp、Date构造函数 --> console.log(new Date().constructor):Date、 console.log(/d/.constructor):RegExp.
      // constructor.name:返回构造函数的名字:(11).constructor.name --> Number,是constructor.name,不是constructor
      if (/^(RegExp|Date)$/i.test(constructor.name)) return new constructor(obj);
    
      // 重新创建一个constructor类的实例,用于保存克隆的数据
      // 不能写成 let clone = {},因为这样写就是往对象里克隆,而有时候克隆的是数组,so应该往obj同样的数据类型的实例中克隆
      let clone = new constructor();
    
      for (let key in obj) {
        if (!obj.hasOwnProperty(key)) break;
        // 如果不递归,只会实现浅拷贝。如果obj[key]是引用类型,需要重新cloneDeep。不管是基本、引用类型,都cloneDeep。
        clone[key] = cloneDeep(obj[key]);
      }
      return clone;
    }
    
    let newArr = cloneDeep(arr);
    console.log(newArr);
    console.log(newArr === arr); // => false
    console.log(newArr[2] === arr[2]); // => false 
    
    let newObj = cloneDeep(obj);
    console.log(newObj);
    console.log(newObj === obj); // => false
    console.log(newObj.d === obj.d); // => false
    
  • 相关阅读:
    canvas之碎碎念
    canvas之动态时钟
    属性小问题
    readonly and disabled
    table
    地图热区
    子块元素在父块元素中居中
    Ajax与JS
    前端测试
    html5/css3
  • 原文地址:https://www.cnblogs.com/jianjie/p/13865989.html
Copyright © 2011-2022 走看看