javascript - 使用 Object.fromEntries() 深度克隆一个对象

标签 javascript arrays json object ecmascript-6

前段时间,我读到关于新方法的提议 Object.fromEntries()某些浏览器的较新版本支持 ( reference )。在阅读它时,我想到了使用此方法深度克隆一个对象的想法,而不是使用JSON.parse(JSON.stringify(目标))。到目前为止,我已经提出了下一个递归方法,从我的 Angular 来看它似乎可行。

const obj = {
  key1: {key11: "key11", key12: "key12", key13: {key131: 22}},
  key2: {key21: "key21", key22: "key22"},
  key3: "key3",
  key4: [1,2,3,4]
}

const cloneObj = (obj) =>
{
    if (typeof obj !== "object")
       return obj;
    else if (Array.isArray(obj))
       return obj.slice();

    return Object.fromEntries(Object.entries(obj).map(
        ([k,v]) => ([k, cloneObj(v)])
    ));
}

// Clone the original object.
let newObj = cloneObj(obj);

// Make changes on the original object.
obj.key1.key11 = "TEST";
obj.key3 = "TEST";
obj.key1.key13.key131 = "TEST";
obj.key4[1] = "TEST";

// Display both objects on the console.
console.log("Original object: ", obj);
console.log("Cloned object: ", newObj);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}

但是,我怀疑我是否没有遗漏一些东西来完成对象的深度克隆。所以我的问题是:

1) 为了完成对象的深度克隆,我遗漏了一些重要的东西?

2) 如果方法没问题,你认为这比使用 JSON.parse()JSON.stringify( )?

提前致谢!


更新1

这是包含对答案提供的反馈的更新版本:

const obj = {
  key1: {key11: "key11", key12: "key12", key13: {key131: 22}},
  key2: {key21: "key21", key22: "key22"},
  key3: "key3",
  key4: [1,2,3,{key: "value"}]
}

const cloneObj = (obj) =>
{
    if (Object(obj) !== obj)
       return obj;
    else if (Array.isArray(obj))
       return obj.map(cloneObj);

    return Object.fromEntries(Object.entries(obj).map(
        ([k,v]) => ([k, cloneObj(v)])
    ));
}

// Clone the original object.
let newObj = cloneObj(obj);

// Make changes on the original object.
obj.key1.key11 = "TEST";
obj.key3 = "TEST";
obj.key1.key13.key131 = "TEST";
obj.key4[1] = "TEST";
obj.key4[3].key = "TEST";

// Display both objects on the console.
console.log("Original object: ", obj);
console.log("Cloned object: ", newObj);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}

更新2

添加了与我的第二个问题相关的性能比较:

Tested on Firefox 66.0.3 (64bits):
Test_JSON: 1923.000ms
Test_cloneObj: 2047.000ms

Tested on Chrome 73.0.3683.103 (64 bits):
Test_JSON: 2276.560ms
Test_cloneObj: 1903.675ms

const cloneObj = (obj) =>
{
    if (Object(obj) !== obj)
       return obj;
    else if (Array.isArray(obj))
       return obj.map(cloneObj);

    return Object.fromEntries(Object.entries(obj).map(
        ([k,v]) => ([k, cloneObj(v)])
    ));
}

// Generate an object.

const getRandom = (min, max) => Math.floor(Math.random() * (max - min) + min);

let obj = {};

for (let i = 0; i < 100000; i++)
{
    obj["Array" + i] = Array.from({length: 100}, () => getRandom(0, 1000));
    obj["Obj" + i] = {"key": getRandom(0, 1000)};
    obj["Const" + i] = "some_string";
}

// Test performance on JSON.parse()/stringify()

console.time("Test_JSON");
let obj1 = JSON.parse(JSON.stringify(obj));
console.timeEnd("Test_JSON");

// Test performance on cloneObj().

console.time("Test_cloneObj");
let obj2 = cloneObj(obj);
console.timeEnd("Test_cloneObj");
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}

最佳答案

你错过了一件事:

else if (Array.isArray(obj))
  return obj.slice();

这将返回数组的浅拷贝。如果数组包含对象,则不会克隆那些底层对象:

const obj = [
  ['foo']
];

const cloneObj = (obj) =>
{
    if (typeof obj !== "object")
       return obj;
    else if (Array.isArray(obj))
       return obj.slice();

    return Object.fromEntries(Object.entries(obj).map(
        ([k,v]) => ([k, cloneObj(v)])
    ));
}

// Clone the original object.
let newObj = cloneObj(obj);

// Make changes on the original object.
obj[0][0] = 'bar';

// Display both objects on the console.
console.log("Original object: ", obj);
console.log("Cloned object: ", newObj);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}

要修复它,返回 obj.map(cloneObj); 代替:

const obj = [
  ['foo']
];

const cloneObj = (obj) =>
{
    if (typeof obj !== "object")
       return obj;
    else if (Array.isArray(obj))
       return obj.map(cloneObj);

    return Object.fromEntries(Object.entries(obj).map(
        ([k,v]) => ([k, cloneObj(v)])
    ));
}

// Clone the original object.
let newObj = cloneObj(obj);

// Make changes on the original object.
obj[0][0] = 'bar';

// Display both objects on the console.
console.log("Original object: ", obj);
console.log("Cloned object: ", newObj);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}

关于javascript - 使用 Object.fromEntries() 深度克隆一个对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55700810/

相关文章:

javascript - 如何将passport-jwt与ACL(访问控制列表)模式结合起来

c++ - 如何使用此特定容器在 C++ 中创建二维数组

java - 我可以将paintComponents() 添加到数组中吗?

python - HTTP 请求为 params 中的一个键分配多个值

json - 如何在 Gin 路由器中呈现静态文件?

javascript - Node.js socket.io 的 Dojox 套接字异常

javascript - 如何根据某些条件对数组进行部分排序?

javascript - 使用 jQuery 检测按键何时被按下然后释放

javascript - unique() 函数如何工作

javascript - JSON 或 Jquery 错误 : Uncaught TypeError: Cannot read property 'error' of null