在 Javascript/Type 中克隆一个数组

我有两个对象的数组:

genericItems: Item[] = [];
backupData: Item[] = [];

我用 genericItems数据填充 HTML 表。该表是可修改的。有一个重置按钮,可以撤消对 backUpData所做的所有更改。这个数组由一个服务填充:

getGenericItems(selected: Item) {
this.itemService.getGenericItems(selected).subscribe(
result => {
this.genericItems = result;
});
this.backupData = this.genericItems.slice();
}

我的想法是,用户的变化将得到反映在第一个数组和第二个数组可以作为备份复位操作。我在这里面临的问题是当用户修改表时(genericItems[]),第二个数组 backupData也被修改)。

这是如何发生的,如何防止这种情况?

246173 次浏览

试试这个:

[https://lodash.com/docs/4.17.4#clone][1]


var objects = [{ 'a': 1 }, { 'b': 2 }];


var shallow = _.clone(objects);
console.log(shallow[0] === objects[0]);
// => true

看起来您可能在复制 Array 的位置上犯了一个错误。看看我下面的解释和对代码的轻微修改,这将有助于您将数据重置为以前的状态。

在你的例子中,我可以看到以下情况:

  • 您正在执行获取通用项的请求
  • 获取数据之后,将结果设置为 this.genericItems
  • 然后直接将 backupData 设置为结果

你不希望第三点按这个顺序发生,对吗?

这样会不会更好:

  • 你来处理数据请求
  • 为 this 中的当前内容制作一个备份副本
  • 然后根据请求设置 genericItems

试试这个:

getGenericItems(selected: Item) {
this.itemService.getGenericItems(selected).subscribe(
result => {
// make a backup before you change the genericItems
this.backupData = this.genericItems.slice();


// now update genericItems with the results from your request
this.genericItems = result;
});
}

代码中的下面一行创建一个新数组,将所有对象引用从 genericItems复制到该新数组中,并将其分配给 backupData:

this.backupData = this.genericItems.slice();

因此,虽然 backupDatagenericItems是不同的数组,但它们包含相同的对象引用。

您可以引入一个库来为您进行深度复制(正如@LatinWar 所提到的)。

但是如果 Item不太复杂,也许你可以添加一个 clone方法来深度克隆这个对象:

class Item {
somePrimitiveType: string;
someRefType: any = { someProperty: 0 };


clone(): Item {
let clone = new Item();


// Assignment will copy primitive types


clone.somePrimitiveType = this.somePrimitiveType;


// Explicitly deep copy the reference types


clone.someRefType = {
someProperty: this.someRefType.someProperty
};


return clone;
}
}

然后在每个项目上调用 clone():

this.backupData = this.genericItems.map(item => item.clone());

看起来你想要的是对象的 收到。为什么不使用 Object.assign()?不需要图书馆,而且只有一句话:)

getGenericItems(selected: Item) {
this.itemService.getGenericItems(selected).subscribe(
result => {
this.genericItems = result;
this.backupDate = Object.assign({}, result);
//this.backupdate WILL NOT share the same memory locations as this.genericItems
//modifying this.genericItems WILL NOT modify this.backupdate
});
}

更多关于 Object.assign(): https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign的资料

数组副本解释-深度和浅度

下面的代码可能有助于您复制第一级对象

let original = [{ a: 1 }, {b:1}]
const copy = [ ...original ].map(item=>({...item}))

因此对于下面的情况,值保持不变

copy[0].a = 23
console.log(original[0].a) //logs 1 -- value didn't change voila :)

这个案子失败了

let original = [{ a: {b:2} }, {b:1}]
const copy = [ ...original ].map(item=>({...item}))
copy[0].a.b = 23;
console.log(original[0].a) //logs 23 -- lost the original one :(

尝试分离的 ES 模块-克隆深度:

我建议使用 loash cloneDeep API (< em > This can be 作为 独立模块安装,减少了 treeshaking 的代码占用,这有助于您复制对象 内部对象完全脱离原始对象的引用。 作为另一种选择,你可以依靠 JSON.stringifyJSON.parse 方法来解除引用快速和性能太。

参考文档: < a href = “ https://github.com/loash/loash”rel = “ nofollow norefrer”> https://github.com/lodash/lodash

个人软件包 : < a href = “ https://www.npmjs.com/Package/lodash.clonedeep”rel = “ nofollow norefrer”> https://www.npmjs.com/Package/lodash.clonedeep

我对 primeNg 数据表也有同样的问题。在尝试和哭泣之后,我用这段代码解决了这个问题。

private deepArrayCopy(arr: SelectItem[]): SelectItem[] {
const result: SelectItem[] = [];
if (!arr) {
return result;
}
const arrayLength = arr.length;
for (let i = 0; i <= arrayLength; i++) {
const item = arr[i];
if (item) {
result.push({ label: item.label, value: item.value });
}
}
return result;
}

用于初始化备份值

backupData = this.deepArrayCopy(genericItems);

用于重置更改

genericItems = this.deepArrayCopy(backupData);

关键是通过使用 {}而不是调用构造函数来重新创建项。 我试过 new SelectItem(item.label, item.value),但是没有用。

克隆一个对象:

const myClonedObject = Object.assign({}, myObject);

克隆一个数组:

  • 选项1 如果您有一个基元类型数组:

const myClonedArray = Object.assign([], myArray);

  • 选项2 -如果你有一个对象数组:
const myArray= [{ a: 'a', b: 'b' }, { a: 'c', b: 'd' }];
const myClonedArray = [];
myArray.forEach(val => myClonedArray.push(Object.assign({}, val)));

克隆数组最简单的方法是

backUpData = genericItems.concat();

这将为数组索引创建一个新内存

使用 map 或其他类似的解决方案无助于深度克隆对象数组。 在不添加新库的情况下,一种更简单的方法是使用 JSON.stringfy 和 JSON.parse。

在你的情况下,这应该可行:

this.backupData = JSON.parse(JSON.stringify(genericItems));

试试这个

const returnedTarget = Object.assign(target, source);

并将空数组传递给目标

以防复杂对象这种方法对我有用

数组情况下的 $.extend(true, [], originalArray)

对象情况下的 $.extend(true, {}, originalObject)

Javascript 中的克隆数组和对象有不同的语法 。迟早每个人都会知道这其中的区别,然后在这里结束。

打印稿ES6中,可以对数组和对象使用扩展运算符:

const myClonedArray  = [...myArray];  // This is ok for [1,2,'test','bla']
// But wont work for [{a:1}, {b:2}].
// A bug will occur when you
// modify the clone and you expect the
// original not to be modified.
// The solution is to do a deep copy
// when you are cloning an array of objects.

要做一个对象的深拷贝,你需要一个外部库:

import {cloneDeep} from 'lodash';
const myClonedArray = cloneDeep(myArray);     // This works for [{a:1}, {b:2}]

扩展操作符也可以对对象进行操作,但是它只会做一个浅拷贝(第一层子层)

const myShallowClonedObject = {...myObject};   // Will do a shallow copy
// and cause you an un expected bug.

要做一个对象的深拷贝,你需要一个外部库:

 import {cloneDeep} from 'lodash';
const deeplyClonedObject = cloneDeep(myObject);   // This works for [{a:{b:2}}]

你可以使用映射函数

 toArray= fromArray.map(x => x);

如果数组中的项不是原语,则可以使用扩展运算符进行此操作。

this.plansCopy = this.plans.map(obj => ({...obj}));

完整答案: https://stackoverflow.com/a/47776875/5775048

尝试以下代码:

this.cloneArray= [...this.OriginalArray]

以一种非常强大的方式克隆一个对象/数组(没有引用)

您可以使用 @angular-devkit获得 object/array的深拷贝。

import { deepCopy } from '@angular-devkit/core/src/utils/object';


export class AppComponent {


object = { .. some object data .. }
array = [ .. some list data .. ]


constructor() {
const newObject = deepCopy(this.object);
const newArray = deepCopy(this.array);
}
}

数组级别的克隆解决方案不适合我,添加了这个克隆实用程序的类型,它做了一个更深的克隆,在我的情况下工作

export function cloneArray<T>(arr: T[]): T[] {
return arr.map((x) => Object.assign({}, x))
}

“不工作”我的意思是,如果我传递一个数组到一个函数 [...arr]Object.assign([], arr)arr.splice()它仍然是变异的原始数组