数组浅拷贝和深拷贝

乞丐版深拷贝

  • 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;
  }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容