zl程序教程

您现在的位置是:首页 >  后端

当前栏目

js对象拷贝方法

2023-09-14 09:07:43 时间

Object.create(),浅拷贝

const clone = Object.create(
  Object.getPrototypeOf(obj),   
  Object.getOwnPropertyDescriptors(obj)
);

Object.assign(targetObj,sourceObj),浅拷贝

不是深拷贝,循环引用、各种数据类型都可以拷贝,引用类型不是深拷贝

  • 它不会拷贝对象的继承属性;
  • 它不会拷贝对象的不可枚举的属性;
  • 不可以拷贝对象中的对象;
  • 可以拷贝 Symbol 类型的属性;
  • 无法正确拷贝属性和属性
  • 可以拷贝undefined/boolean/null/function/Date/RegExp/array/array中的对象;

参考阮一峰文档: https://es6.ruanyifeng.com/#docs/object-methods#Object-assign

扩展运算符,浅拷贝

不是深拷贝,循环引用、各种数据类型都可以拷贝,引用类型不是深拷贝

JSON.parse(JSON.stringfiy()),不完全深拷贝

  • 拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,经过 JSON.stringify 序列化之后的字符串中这个键值对会消失;
  • 拷贝 Date 引用类型会变成字符串;
  • 无法拷贝不可枚举的属性;
  • 无法拷贝对象的原型链;
  • 拷贝 RegExp 引用类型会变成空对象;
  • 对象中含有 NaN、Infinity 以及 -Infinity,JSON 序列化的结果会变成 null;
  • 无法拷贝对象的循环引用,如果对象中有循环引用,会报错:
Uncaught TypeError: Converting circular structure to JSON

递归

// 待拷贝的对象
let originObj = {
    re: /hello/,
    ff: function () {},
    sym: Symbol(123),
    date: new Date(),
    mp: new Map(),
    st: new Set(),
    a: "aaa",
    b: 123,
    c: true,
    d: undefined,
    e: null,
    f: {
        f1: "fff",
        f2: { a: "aaa", b: 123, c: true, d: undefined, e: null },
        f3: [{ a: "aaa", b: 123, c: true, d: undefined, e: null }, "f666", 666],
    },
    g: [1, 2, 3],
    h: [{ a: "aaa", b: 123, c: true, d: undefined, e: null }, "f666", 666],
};
var obj222 = {
    a: originObj,
};
originObj.obj222 = obj222;
// 简单实现,缺点:没有考虑 Date/RegExp/Set/Map/循环引用,如果有循环引用会报错栈溢出
function cloneDeep(obj) {
    let objClone = obj.constructor === Array ? [] : Object.create({});
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            // Object.prototype.toString.call(/123/)
            if (obj[key] && typeof obj[key] === "object") {
                objClone[key] = cloneDeep(obj[key]);
            } else {
                objClone[key] = obj[key];
            }
        }
    }
    return objClone;
}
// 详细实现
/**
 * 深拷贝关注点:
 * 1. JavaScript内置对象的复制: Set、Map、Date、RegExp等
 * 2. 循环引用问题
 * @param {*} object
 * @returns
 */
function deepClone(source, memory) {
    const isPrimitive = (value) => {
        return /Number|Boolean|String|Null|Undefined|Symbol|Function/.test(
            Object.prototype.toString.call(value)
        );
    };
    let result = null;
    let type = Object.prototype.toString.call(source);
    memory || (memory = new WeakMap());
    // 原始数据类型及函数
    if (isPrimitive(source)) {
        // console.log("current copy is primitive", source);
        result = source;
    }
    // 数组
    else if (Array.isArray(source)) {
        result = source.map((value) => deepClone(value, memory));
    }
    // 内置对象Date、Regex
    else if (type === "[object Date]") {
        result = new Date(source);
    } else if (type === "[object RegExp]") {
        result = new RegExp(source);
    }
    // 内置对象Set、Map
    else if (type === "[object Set]") {
        result = new Set();
        for (const value of source) {
            result.add(deepClone(value, memory));
        }
    } else if (type === "[object Map]") {
        result = new Map();
        for (const [key, value] of source.entries()) {
            result.set(key, deepClone(value, memory));
        }
    }
    // 引用类型
    else {
        if (memory.has(source)) {
            result = memory.get(source);
        } else {
            result = Object.create(null);
            memory.set(source, result);
            Object.keys(source).forEach((key) => {
                const value = source[key];
                result[key] = deepClone(value, memory);
            });
        }
    }
    return result;
}

console.log(cloneDeep(originObj));
console.log(deepClone(originObj));

参考:https://github.com/shfshanyue/Daily-Question/issues/203#issuecomment-888238489