在 JavaScript 中复制对象

2025-06-09

在 JavaScript 中复制对象

设想:

每当我们在组件之间传递对象作为 props 或函数中的参数时,我们都需要复制该对象以确保它不会影响原始对象。现在,我们可以轻松地根据需要对复制的对象进行任何更改。

因此,我们可以使用以下方法来实现这一点:

  • 扩展运算符
  • 对象分配
  • JSON.parse 和 JSON.stringify

使用这些方法中的任何一个都会完全复制对象吗?它还会复制嵌套对象吗?

我们来看另一个例子:

let deepObj = {a: 4: b: {name: 'react'}, d: {name: 'angular'}};
Enter fullscreen mode Exit fullscreen mode

嵌套对象也是如此deepObj,当涉及复制嵌套对象(即具有值作为引用的对象)时,就会出现浅复制和深复制的概念。

  • 浅拷贝:仅复制一个级别,这意味着如果任何值是引用类型,则复制引用,但不会在新对象中复制确切值。

  • 深度复制:复制每一级嵌套值,即使它是一个引用类型,就像我们deepObj上面的对象示例一样。

让我们逐个尝试复制对象:

1. 扩展运算符 & Object.assign():

例 1:

let obj1 = {a: 3, b: 4, c: "react"}, copiedObj1 = {};

copiedObj1 = {...obj1};
copiedObj1.c = "angular";

console.log(copiedObj1, obj1); 
{a: 3, b: 4, c: "angular"}
{a: 3, b: 4, c: "react"}



let obj2 = {a: 3, b: 4, c: "react"}, copiedObj2 = {};

copiedObj2 = Object.assign({}, obj2);
copiedObj2.c = "vue";

console.log(copiedObj2, obj2); 
{a: 3, b: 4, c: "vue"}
{a: 3, b: 4, c: "react"}
Enter fullscreen mode Exit fullscreen mode

两者都可以完美地复制对象,因为对象值中没有引用类型,并且如果您尝试更改任何属性,则不会对复制的对象产生任何影响。

例2:

let obj1 = {a: 3, c: [1,2]}, newObj1 = {};
newObj1 = {...obj1};
newObj1.c.push(5);

console.log(newobj1, obj1); 
{a: 3, c: [1,2,5]} 
{a: 3, c: [1,2,5]}


let obj2 = {a: 3, c: [1,2]}, newObj2 = {};
newObj2 = Object.assign({}, obj2);
newObj2.c.push(5);

console.log(newobj2, obj2); 
{a: 3, c: [1,2,5]}
{a: 3, c: [1,2,5]}
Enter fullscreen mode Exit fullscreen mode

这里属性 c 的值在两个对象中都变成了 [1,2,5],所以由于是引用类型,即数组 ([1,2]),所以复制操作并不完全正确。它只是复制了对数组的引用。因此Spread operator and Object.assign() only does shallow copying not deep copying.

2. JSON.parse() 和 JSON.stringify():

var obj1 = {a: 3, b: 4, c: "react"};
var copiedObj1 = JSON.parse(JSON.stringify(obj1));
copiedObj1.c = "node";

console.log(copiedObj1, obj1); 
{a: 3, b: 4, c: "node"}
{a: 3, b: 4, c: "react"}


var obj2 = {a: 3, c: [1,2]};
var copiedObj2 = JSON.parse(JSON.stringify(obj2));
copiedObj2.c.push(5);

console.log(copiedObj2 , obj2);
{a: 3, c: [1,2,5]} 
{a: 3, c: [1,2]}
Enter fullscreen mode Exit fullscreen mode

这完美地复制了对象,因为在两种情况下复制对象中的任何更改都不会对原始对象产生任何影响。

但这仅适用于先将值转换为字符串,然后再进行解析的情况。
以下是一些复制对象失败的情况。

let obj = {
  name: 'laptop',
  value: function () {
    return 100000';
  }
}

let copiedObj = JSON.parse(JSON.stringify(obj));

console.log(copiedObj);
{name: 'laptop'}
Enter fullscreen mode Exit fullscreen mode

失败->它从复制的对象中删除了值方法。

let obj = {a: undefined, b: new Date()}

let copiedObj = JSON.parse(JSON.stringify(obj));

console.log(copiedObj);
{b: "2020-06-06T16:23:43.910Z"}
Enter fullscreen mode Exit fullscreen mode

失败->删除第一个属性并将日期值转换为字符串;


对于浅拷贝使用

  1. 扩展运算符
  2. 对象.assign()。

对于深度复制

  1. 使用 lodash 库cloneDeep方法 (_.cloneDeep(任何嵌套对象))

  2. 创建一个自定义函数来处理引用类型,如下例所示,仅涵盖一种情况。

function deepCopy(obj) {
    let copiedObj = {};
    for(key in obj) {
        if(Array.isArray(obj[key])) {
            copiedObj[key] = [...obj[key]];
        } else {
            copiedObj[key] = obj[key]
        }
    }
    return copiedObj;
}

var obj = {value1: 5, value2: [1,2,3]};

var copiedObj = deepCopy(obj);
copiedObj.value2.push(5);

console.log(copiedObj , obj);
{value1: 5, value2: [1,2,3,5]}
{value1: 5, value2: [1,2,3]}
Enter fullscreen mode Exit fullscreen mode

更好的方法是使用递归来处理引用类型

因此,存在其他库,它们可以提供良好的性能事件来进行深度克隆,正如您所见,它需要更多的累积,或者您可以创建自定义函数并添加更多边缘情况。

结论:传递的对象中如果有引用类型,请务必留意。最好使用浅复制和深复制。

感谢阅读!

鏂囩珷鏉ユ簮锛�https://dev.to/ip127001/copying-objects-in-javascript-440b
PREV
适合代码新手的 VS Code 插件
NEXT
Web 开发人员构建移动应用程序的最简单方法