typescript - 克隆对象

IT技术 javascript typescript
2021-01-25 22:30:08

我有一个超类,它是Entity许多子类 ( Customer, Product, ProductCategory...)的父类 ( )

我希望在 Typescript 中动态克隆一个包含不同子对象的对象。

例如: aCustomer有不同的Productwho 有ProductCategory

var cust:Customer  = new Customer ();

cust.name = "someName";
cust.products.push(new Product(someId1));
cust.products.push(new Product(someId2));

为了克隆整个对象树,我在 Entity

public clone():any {
    var cloneObj = new this.constructor();
    for (var attribut in this) {
        if(typeof this[attribut] === "object"){
           cloneObj[attribut] = this.clone();
        } else {
           cloneObj[attribut] = this[attribut];
        }
    }
    return cloneObj;
}

new其转换为 javascript 时会出现以下错误:error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.

虽然脚本有效,但我想摆脱转译错误

6个回答

解决具体问题

您可以使用类型断言告诉编译器您更了解:

public clone(): any {
    var cloneObj = new (this.constructor() as any);
    for (var attribut in this) {
        if (typeof this[attribut] === "object") {
            cloneObj[attribut] = this[attribut].clone();
        } else {
            cloneObj[attribut] = this[attribut];
        }
    }
    return cloneObj;
}

克隆

请记住,有时最好编写自己的映射 - 而不是完全动态的。但是,您可以使用一些“克隆”技巧来提供不同的效果。

我将在所有后续示例中使用以下代码:

class Example {
  constructor(public type: string) {

  }
}

class Customer {
  constructor(public name: string, public example: Example) {

  }

  greet() {
    return 'Hello ' + this.name;
  }
}

var customer = new Customer('David', new Example('DavidType'));

选项 1:传播

属性:
方法:否
深层复制:否

var clone = { ...customer };

alert(clone.name + ' ' + clone.example.type); // David DavidType
//alert(clone.greet()); // Not OK

clone.name = 'Steve';
clone.example.type = 'SteveType';

alert(customer.name + ' ' + customer.example.type); // David SteveType

选项 2:Object.assign

属性:
方法:否
深层复制:否

var clone = Object.assign({}, customer);

alert(clone.name + ' ' + clone.example.type); // David DavidType
alert(clone.greet()); // Not OK, although compiler won't spot it

clone.name = 'Steve';
clone.example.type = 'SteveType';

alert(customer.name + ' ' + customer.example.type); // David SteveType

选项 3:Object.create

属性:继承
方法:继承
Deep Copy:Shallow Inherited(深度变化影响原始和克隆)

var clone = Object.create(customer);

alert(clone.name + ' ' + clone.example.type); // David DavidType
alert(clone.greet()); // OK

customer.name = 'Misha';
customer.example = new Example("MishaType");

// clone sees changes to original 
alert(clone.name + ' ' + clone.example.type); // Misha MishaType

clone.name = 'Steve';
clone.example.type = 'SteveType';

// original sees changes to clone
alert(customer.name + ' ' + customer.example.type); // Misha SteveType

选项 4:深度复制功能

属性:
方法:否
深层复制:

function deepCopy(obj) {
    var copy;

    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = deepCopy(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = deepCopy(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

var clone = deepCopy(customer) as Customer;

alert(clone.name + ' ' + clone.example.type); // David DavidType
// alert(clone.greet()); // Not OK - not really a customer

clone.name = 'Steve';
clone.example.type = 'SteveType';

alert(customer.name + ' ' + customer.example.type); // David DavidType
你能不能澄清一下你是如何使用它的?我作为我的对象的方法包含在内,然后得到一个错误,说不是一个函数......
2021-03-20 22:30:08
我收到以下错误:“错误类型错误:this.constructor(...) 不是构造函数”
2021-03-22 22:30:08
关闭,transpile 停止抱怨 typescript 1.3,但是一旦在 javascript 中它就会抛出错误。Typescript 1.4.1,不会放过。
2021-04-04 22:30:08
你刚刚从那个客户那里做了一个公开的例子吗?
2021-04-07 22:30:08
有人可以为我 TL; DR 在所有答案中给出的哪个解决方案保留了克隆的 OO 类型,即cloned instanceof MyClass === true
2021-04-10 22:30:08

1.使用扩展运算符

const obj1 = { param: "value" };
const obj2 = { ...obj1 };

扩展运算符从 obj1 获取所有字段并将它们扩展到 obj2。在结果中,您将获得具有新引用和与原始字段相同的字段的新对象。

请记住,它是浅拷贝,这意味着如果对象是嵌套的,那么其嵌套的复合参数将通过相同的引用存在于新对象中。

2.Object.assign()

const obj1={ param: "value" };
const obj2:any = Object.assign({}, obj1);

Object.assign创建真正的副本,但只拥有自己的属性,因此原型中的属性不会存在于复制的对象中。也是浅拷贝。


3.Object.create()

const obj1={ param: "value" };
const obj2:any = Object.create(obj1);

Object.create 不是在做真正的克隆,而是从原型创建对象。因此,如果对象应该克隆主要类型属性,请使用它,因为主要类型属性分配不是通过引用完成的。

的加号的Object.create是在原型声明的任何功能都将在我们新创建的对象可用。


关于浅拷贝的几件事

浅拷贝将旧对象的所有字段放入新对象中,但这也意味着如果原始对象具有复合类型字段(对象、数组等),那么这些字段将放入具有相同引用的新对象中。原始对象中的此类字段的突变将反映在新对象中。

这可能看起来像一个陷阱,但真正需要复制整个复杂对象的情况很少见。浅拷贝将重用大部分内存,这意味着与深拷贝相比,它非常便宜。


深拷贝

展开运算符可以方便地进行深复制。

const obj1 = { param: "value", complex: { name: "John"}}
const obj2 = { ...obj1, complex: {...obj1.complex}};

上面的代码创建了 obj1 的深层副本。复合字段“complex”也被复制到 obj2 中。突变字段“复杂”不会反映副本。

你还会看到 ES2015 和typescript开发人员这样做,它从第一个参数(在我的例子中是一个空参数)创建一个对象,并从第二个和后续参数中复制属性): let b = Object.assign({}, a);
2021-03-19 22:30:08
@KenRimple 您 100% 正确,我添加了更多信息。
2021-03-25 22:30:08
Object.assign 将为深层对象创建问题。例如 {name: 'x', values: ['a','b','c']}。使用 Object.assign 进行克隆后,两个对象共享值数组,因此更新一个会影响另一个。请参阅:developer.mozilla.org/en/docs/Web/JavaScript/Reference/...(“深度克隆警告”部分)。它说:对于深度克隆,我们需要使用其他替代方案。这是因为当被分配的属性是一个对象时, Object.assign() 会复制属性引用。
2021-03-27 22:30:08
我不认为这是完全正确的。Object.create(obj1)创建一个新对象并指定 obj1 作为原型。obj1 中的任何字段都不会被复制或克隆。因此,在不修改 obj2 的情况下会看到 obj1 上的更改,因为它本质上没有任何属性。如果您首先修改 obj2,将不会看到您定义的字段的原型,因为 obj2 的具有名称的字段在层次结构中更接近。
2021-04-02 22:30:08
2021-04-11 22:30:08

试试这个:

let copy = (JSON.parse(JSON.stringify(objectToCopy)));

这是一个很好的解决方案,直到您使用非常大的对象或您的对象具有不可序列化的属性。

为了保持类型安全,您可以在要从中进行复制的类中使用复制函数:

getCopy(): YourClassName{
    return (JSON.parse(JSON.stringify(this)));
}

或以静态方式:

static createCopy(objectToCopy: YourClassName): YourClassName{
    return (JSON.parse(JSON.stringify(objectToCopy)));
}
另外这似乎不太有效的比较所提供的功能deepcopy的上面
2021-03-17 22:30:08
这没问题,但您应该记住,在序列化/解析时,您将丢失原型信息和 json 中不支持的所有类型。
2021-03-26 22:30:08
仅适用于 98% 的情况。undefined至少会导致丢失带值的如果objectToCopy = { x : undefined};然后在运行您的代码之后Object.keys(objectToCopy).length1Object.keys(copy).length而是0
2021-04-03 22:30:08
当我使用“(JSON.parse(JSON.stringify(objectToCopy)));”时,出现此错误:“将循环结构转换为JSON”
2021-04-08 22:30:08

TypeScript/JavaScript 有自己的浅层克隆操作符:

let shallowClone = { ...original };

使用 TypeScript 2.1 中引入的“Object Spread”很容易获得浅拷贝

这个typescript: let copy = { ...original };

产生这个 JavaScript:

var __assign = (this && this.__assign) || Object.assign || function(t) {
    for (var s, i = 1, n = arguments.length; i < n; i++) {
        s = arguments[i];
        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
            t[p] = s[p];
    }
    return t;
};
var copy = __assign({}, original);

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html

注意:这将创建一个浅拷贝
2021-04-11 22:30:08