【实践】深拷贝方法总结

发布于:2024-12-23 ⋅ 阅读:(15) ⋅ 点赞:(0)

前言

深拷贝(Deep Copy)是指创建一个对象或数据结构的完全独立副本,使得新对象与原对象之间没有任何引用关系。对于嵌套的对象或数组,深拷贝会递归地复制每个层级的内容。
本文汇总了在实践中积累的方法与经验,若其中有任何不准确或有待商榷之处,欢迎各位读者批评指正。

1. 使用 structuredClone()

从 ECMAScript 2023 (ES14) 开始引入,structuredClone() 方法可以执行结构化克隆算法,适用于大多数内置类型的数据结构。

let original = { a: 1, b: { c: 2 } };
let clone = structuredClone(original);

2. 使用 JSON 序列化和反序列化

通过 JSON.stringify() 和 JSON.parse() 来实现深拷贝。

let original = { a: 1, b: { c: 2 } };
let clone = JSON.parse(JSON.stringify(original));

3. 使用第三方库(如 Lodash 的 _.cloneDeep)

let _ = require('lodash');
let original = { a: 1, b: { c: 2 }, d: [3, 4] };
let clone = _.cloneDeep(original);

4. 自定义递归函数

编写自己的递归函数来遍历对象并逐层复制。

// path ../determineDataType/index.mjs
/**
 * @description 数据类型
 * @readonly
 * @enum {String}
 */
export const DataType = {
  Object: "Object",
  Array: "Array",
  Function: "Function",
  String: "String",
  Number: "Number",
  Boolean: "Boolean",
  Undefined: "Undefined",
  Null: "Null",
  Symbol: "Symbol",
  BigInt: "BigInt",
  Map: "Map",
  Set: "Set",
  WeakMap: "WeakMap",
  WeakSet: "WeakSet",
  Date: "Date",
  Error: "Error",
  Window: "Window",
  Global: "Global",
  RegExp: "RegExp",
};

/**
 * @description 类型映射表
 * @readonly
 * @type {Map<string,DataType>}
 */
const DataTypeMap = new Map([
  ["[object Object]", DataType.Object],
  ["[object Array]", DataType.Array],
  ["[object Function]", DataType.Function],
  ["[object String]", DataType.String],
  ["[object Number]", DataType.Number],
  ["[object Boolean]", DataType.Boolean],
  ["[object Undefined]", DataType.Undefined],
  ["[object Null]", DataType.Null],
  ["[object Symbol]", DataType.Symbol],
  ["[object BigInt]", DataType.BigInt],
  ["[object Map]", DataType.Map],
  ["[object Set]", DataType.Set],
  ["[object WeakMap]", DataType.WeakMap],
  ["[object WeakSet]", DataType.WeakSet],
  ["[object Date]", DataType.Date],
  ["[object Error]", DataType.Error],
  ["[object Window]", DataType.Window],
  ["[object Global]", DataType.Global],
  ["[object RegExp]", DataType.RegExp],
]);

/**
 * @description 判断数据类型
 * @param {any} obj
 * @returns {DataType}
 */
export const determineDataType = (obj) =>
  DataTypeMap.get(Object.prototype.toString.call(obj));
import { DataType, determineDataType } from "../determineDataType/index.mjs";

/**
 * @description 支持的数据类型
 * @type {Array<DataType>}
 */
const supportDataType = [
  DataType.Object,
  DataType.Array,
  DataType.String,
  DataType.Number,
  DataType.Boolean,
  DataType.Undefined,
  DataType.Null,
  DataType.BigInt,
  DataType.Date,
  DataType.Map,
];

/**
 * @description 深拷贝
 * @param {Object} obj
 * @returns {Object | null}
 */
export const deepClone = (obj) => {
  const dataType = determineDataType(obj);
  if (
    dataType !== DataType.Object &&
    dataType !== DataType.Array &&
    dataType !== DataType.Map
  ) {
    console.warn("It only supports Object,Array,Map.");
    return null;
  }

  /**
   * @description 克隆结果
   * @type {any}
   */
  let result;

  /**
   *
   * @param {any} value
   * @param {string | number} key
   * @param {any} newTarget
   */
  const cloneLogic = (value, key, newTarget) => {
    const newTargetDataType = determineDataType(newTarget);

    /**
     * @description 写值
     * @param {any} _key
     * @param {any} _value
     */
    const setNewTargetValue = (_key, _value) => {
      if (
        newTargetDataType === DataType.Object ||
        newTargetDataType === DataType.Array
      ) {
        newTarget[_key] = _value;
      } else if (newTargetDataType === DataType.Map) {
        newTarget.set(_key, _value);
      }
    };

    /**
     * @description 读值
     * @param {any} _key
     * @returns {any}
     */
    const getNewTargetValue = (_key) => {
      if (
        newTargetDataType === DataType.Object ||
        newTargetDataType === DataType.Array
      ) {
        return newTarget[_key];
      } else if (newTargetDataType === DataType.Map) {
        return newTarget.get(_key);
      }
    };

    const valueDataType = determineDataType(value);
    if (supportDataType.includes(valueDataType)) {
      if (valueDataType === DataType.Object) {
        if (value === obj) {
          setNewTargetValue(key, newTarget);
        } else {
          setNewTargetValue(key, {});
          deepCloneObject(value, getNewTargetValue(key));
        }
      } else if (valueDataType === DataType.Array) {
        setNewTargetValue(key, []);
        deepCloneArray(value, getNewTargetValue(key));
      } else if (valueDataType === DataType.Date) {
        setNewTargetValue(key, new Date(value));
      } else {
        setNewTargetValue(key, value);
      }
    } else {
      console.warn(
        `${key}'s DataType is ${valueDataType}. It only supports ${supportDataType.join(
          ","
        )}`
      );
    }
  };

  /**
   * @description 深拷贝对象
   * @param {Object} target
   * @param {Object} newObj
   */
  const deepCloneObject = (target, newObj) => {
    const keys = Object.keys(target);
    for (let index = 0; index < keys.length; index++) {
      const key = keys[index];
      cloneLogic(target[key], key, newObj);
    }
  };

  /**
   * @description 深拷贝数组
   * @param {Array} target
   * @param {Array} newArr
   */
  const deepCloneArray = (target, newArr) => {
    for (let index = 0; index < target.length; index++) {
      cloneLogic(target[index], index, newArr);
    }
  };

  /**
   * @description 深拷贝Map
   * @param {Map} target
   * @param {Map} newMap
   */
  const deepCloneMap = (target, newMap) => {
    for (const [key, value] of target) {
      cloneLogic(value, key, newMap);
    }
  };

  if (dataType === DataType.Object) {
    result = {};
    deepCloneObject(obj, result);
  } else if (dataType === DataType.Array) {
    result = [];
    deepCloneArray(obj, result);
  } else if (dataType === DataType.Map) {
    result = new Map();
    deepCloneMap(obj, result);
  }

  return result;
};
// test
import { deepClone } from "./index.mjs";
let original = { a: 1, b: { c: 2 } };
original.self = original;
let clone = deepClone(original);

网站公告

今日签到

点亮在社区的每一天
去签到