美文网首页
数组浅拷贝和深拷贝

数组浅拷贝和深拷贝

作者: 李幸娟 | 来源:发表于2019-10-21 17:16 被阅读0次

    乞丐版深拷贝

    • JSON.parse(JSON.stringify())
    • 缺点:无法处理循环引用问题
    const obj = {
      name:'red',
      age:18
    }
    obj.cir = obj
    // 这时候的 obj其实是
    // obj {
    //   name: "red",
    //   age: 18,
    //   cir: {
    //     name: "red",
    //     age: 18,
    //     cir: {
    //       name: "red",
    //       age: 18,
    //       cir:......
    //     }
    //   }
    // };
    JSON.parse(JSON.stringify(obj))
    // 报错:Uncaught TypeError: Converting circular structure to JSON
    

    我发现使用map也可以实现深拷贝

    • 但是应该有很多种情况没有考虑,比如传进来的如果不是对象
    function clone(source) {
      const iterableArr = Object.entries(source);
      return Object.fromEntries(new Map(iterableArr));
    }
    

    略微升级版

    • 但是应该是很多很多情况没有考虑
    /**
     * @des 深拷贝
     * @param {*} source 要被克隆的对像
     * @param {Map} map 
     * @return: 
     */
    function clone(source, map = new WeakMap()) {
      if (typeof source === "object") {
        let target = Array.isArray(source) ? [] : {};
        if (map.get(source)) {
          return map.get(source);
        }
        map.set(source, target);
        for (let [key, value] of Object.entries(source)) {
          target[key] = clone(value, map);
        }
        return target;
      } else {
        return source;
      }
    }
    

    lodash的深拷贝+理解注释

    import { isObject, isBuffer } from "util";
    import copyArray from "./copyArray.js";
    
    /* 用于编写用于克隆的位掩码 */
    const CLONE_DEEP_FLAG = 1;
    const CLONE_FLAT_FLAG = 2;
    const CLONE_SYMBOLS_FLAG = 4;
    
    /* `Object#toString` 的结果引用 */
    const argsTag = "[object Arguments]";
    const arrayTag = "[object Array]";
    const boolTag = "[object Boolean]";
    const dateTag = "[object Date]";
    const errorTag = "[object Error]";
    const mapTag = "[object Map]";
    const numberTag = "[object Number]";
    const objectTag = "[object Object]";
    const regexpTag = "[object RegExp]";
    const setTag = "[object Set]";
    const stringTag = "[object String]";
    const symbolTag = "[object Symbol]";
    const weakMapTag = "[object WeakMap]";
    
    const arrayBufferTag = "[object ArrayBuffer]";
    const dataViewTag = "[object DataView]";
    const float32Tag = "[object Float32Array]";
    const float64Tag = "[object Float64Array]";
    const int8Tag = "[object Int8Array]";
    const int16Tag = "[object Int16Array]";
    const int32Tag = "[object Int32Array]";
    const uint8Tag = "[object Uint8Array]";
    const uint8ClampedTag = "[object Uint8ClampedArray]";
    const uint16Tag = "[object Uint16Array]";
    const uint32Tag = "[object Uint32Array]";
    
    /* 用于标识 `clone` 支持的 `toStringTag` 值 */
    const cloneableTags = {};
    cloneableTags[argsTag] = cloneableTags[arrayTag] = cloneableTags[arrayBufferTag] = cloneableTags[
      dataViewTag
    ] = cloneableTags[boolTag] = cloneableTags[dateTag] = cloneableTags[float32Tag] = cloneableTags[
      float64Tag
    ] = cloneableTags[int8Tag] = cloneableTags[int16Tag] = cloneableTags[int32Tag] = cloneableTags[
      mapTag
    ] = cloneableTags[numberTag] = cloneableTags[objectTag] = cloneableTags[regexpTag] = cloneableTags[
      setTag
    ] = cloneableTags[stringTag] = cloneableTags[symbolTag] = cloneableTags[uint8Tag] = cloneableTags[
      uint8ClampedTag
    ] = cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
    cloneableTags[errorTag] = cloneableTags[weakMapTag] = false;
    
    /* 用于检查对象自身属性中是否有指定的属性 */
    const hasOwnProperty = Object.prototype.hasOwnProperty;
    
    /* 判断参数类型 */
    function getTag(value) {
      if (value == null) {
        return value === undefined ? "[object Undefined]" : "[object Null]";
      }
      Object.prototype.toString.call(value);
    }
    
    /* 初始化一个数组 */
    function initCloneArray(array) {
      const { length } = array;
      const result = new array.constructor(length);
    
      // 添加由 `RegExp#exec` 分配的属性
      // exec() 方法返回一个数组(包含额外的属性 index 和 input属性)
      if (length && typeof array[0] === "string" && hasOwnProperty.call(array, "index")) {
        result.index = array.index;
        result.input = array.input;
      }
      return result;
    }
    
    /**
     * @des 深克隆
     * @param {*}  value 要被克隆的值
     * @param {number}  bitmask 位掩码标识符
     * 1 - 深克隆
     * 2 - 展平属性
     * 4 - 克隆symbol
     * @param {Function} [customizer] 定制的克隆函数.
     * @param {string} [key] `value`的键
     * @param {Object} [object] `value`的父对象.
     * @param {Object} [stack] 跟踪遍历的对象及其克隆副本
     * @return:
     */
    function baseClone(value, bitmask, customizer, key, object, stack) {
      let result;
      // CLONE_DEEP_FLAG => 1 => 0001
      // CLONE_FLAT_FLAG => 2 => 0010
      // CLONE_SYMBOLS_FLAG => 4 => 0100
      // 若bitmask = 1, 则isDeep为true,其他为false
      // 若bitmask = 3, 则isDeep,isFlat为true,isFull为false
      const isDeep = bitmask & CLONE_DEEP_FLAG;
      const isFlat = bitmask & CLONE_FLAT_FLAG;
      const isFull = bitmask & CLONE_SYMBOLS_FLAG;
    
      // 如果用户有定制克隆函数的话,使用用户自定函数进行克隆
      if (customizer) {
        // 若被克隆的`value`有父对象,则将 被克隆的`value`本身,他的`key`,他的父对象传给自定义克隆函数
        // 若无,则仅将 `value` 传给定制函数
        result = object ? customizer(value, key, object, stack) : customizer(value);
      }
      if (result !== undefined) {
        return result;
      }
      // 若被克隆的`value`不是对象,直接返回值即可
      if (!isObject(value)) {
        return value;
      }
      const isArr = Array.isArray(value);
      // function getTag(value) {
      //   if (value == null) {
      //     return value === undefined ? "[object Undefined]" : "[object Null]";
      //   }
      //   Object.prototype.toString.call(value);
      // }
      const tag = getTag(value);
      if (isArr) {
        // 初始化一个空数组
        result = initCloneArray(value);
        if (!isDeep) {
          // 若不是深拷贝的话,调用数组拷贝方法
          // 这个方法先创建一个和 `value` 长度一样的空数组,然后再循环将 `value` 的值赋给新的空数组
          return copyArray(value, result);
        }
      } else {
        // 如果被拷贝 `value` 不是数组,则按照下面的思路进行拷贝
        const isFunc = typeof value === "function";
    
        // 如果是 Buffer类型,则调用 `cloneBuffer` 方法对其克隆
        if (isBuffer(value)) {
          return cloneBuffer(value, isDeep);
        }
    
        // 如果Object#toString 检测的类型是 对象、参数、是函数但是不是对象
        if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
          result = isFlat || isFunc ? {} : initCloneObject(vlaue);
          if (!isDeep) {
            // TODO: 我有点懵了,上面的判断条件和是否深拷贝,为什么调用拷贝 Symbol的函数
            return isFlat
              ? copySymbolsIn(value, copyObject(value, keysIn(value), result))
              : copySymbols(value, Object.assign(result, value));
          }
        } else {
          if (isFunc || !cloneableTags[tag]) {
            // 如果是函数或者是不能克隆的类型
            // TODO: ???没有父对象的话返回 {} ??
            return object ? value : {};
          }
        }
    
        // 检查循环引用并且返回其对应的克隆
        stack || (stack = new stack());
        // 如果栈里面已经有这个对象的话,就直接返回栈里保存的值,
        // 上面的简单版本这里的处理是使用了 `weakMap` 存储
        const stacked = stack.get(value);
        if (stacked) {
          return stacked;
        }
        stack.set(value, result);
    
        if (tag == mapTag) {
          value.forEach(subValue => {
            result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));
          });
          return result;
        }
    
        if (tag == setTag) {
          value.forEach(subValue => {
            result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));
          });
          return result;
        }
    
        // 类型化数组
        if (isTypeArray(value)) {
          return result;
        }
        
        // TODO: 这里完全看不懂了
        const keysFunc = isFull ? (isFlat ? getAllKeysIn : getAllKeys) : isFlat ? keysIn : keys;
        const props = isArr ? undefined : keysFunc(value);
        arrayEach(props || value, (subValue, key) => {
          if (props) {
            key = subValue;
            subValue = value[key];
          }
          // Recursively populate clone (susceptible to call stack limits).
          assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));
        });
        return result;
      }
    }
    
    

    相关文章

      网友评论

          本文标题:数组浅拷贝和深拷贝

          本文链接:https://www.haomeiwen.com/subject/hiztvctx.html