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

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

例如:拥有不同产品的客户拥有一个ProductCategory

var cust:Customer  = new Customer ();

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

为了克隆对象的整个树,我在实体中创建了一个函数

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时,将引发以下错误:错误TS2351:不能对缺少调用或构造签名的表达式使用'new'。

虽然脚本工作,但我想摆脱转译错误


当前回答

我尝试创建一个通用的复制/克隆服务,为嵌套对象保留类型。会爱反馈,如果我做错了什么,但它似乎工作到目前为止…

import { Injectable } from '@angular/core';

@Injectable()
export class CopyService {

  public deepCopy<T>(objectToClone: T): T {
    // If it's a simple type or null, just return it.
    if (typeof objectToClone === 'string' ||
      typeof objectToClone === 'number' ||
      typeof objectToClone === 'undefined' ||
      typeof objectToClone === 'symbol' ||
      typeof objectToClone === 'function' ||
      typeof objectToClone === 'boolean' ||
      objectToClone === null
    ) {
      return objectToClone;
    }

    // Otherwise, check if it has a constructor we can use to properly instantiate it...
    let ctor = Object.getPrototypeOf(objectToClone).constructor;
    if (ctor) {
      let clone = new ctor();

      // Once we've instantiated the correct type, assign the child properties with deep copies of the values
      Object.keys(objectToClone).forEach(key => {
        if (Array.isArray(objectToClone[key]))
          clone[key] = objectToClone[key].map(item => this.deepCopy(item));
        else
          clone[key] = this.deepCopy(objectToClone[key]);
      });

      if (JSON.stringify(objectToClone) !== JSON.stringify(clone))
        console.warn('object cloned, but doesnt match exactly...\nobject: ' + JSON.stringify(objectToClone) + "\nclone: " + JSON.stringify(clone))

      // return our cloned object...
      return clone;
    }
    else {
      //not sure this will ever get hit, but figured I'd have a catch call.
      console.log('deep copy found something it didnt know: ' + JSON.stringify(objectToClone));
      return objectToClone;
    }
  }
}

其他回答

试试这个:

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

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

为了保持类型安全,你可以在你想复制的类中使用一个copy函数:

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

或以静态的方式:

static createCopy(objectToCopy: YourClassName): YourClassName{
    return (JSON.parse(JSON.stringify(objectToCopy)));
}

我自己也遇到过这个问题,最后写了一个小库clone -ts,它提供了一个抽象类,它向任何扩展它的类添加了一个克隆方法。抽象类借用了芬顿接受的答案中描述的深度复制函数,只是替换了Copy = {};使用copy = object. create(originalObj)来保留原始对象的类。下面是一个使用该类的示例。

import {Cloneable, CloneableArgs} from 'cloneable-ts';

// Interface that will be used as named arguments to initialize and clone an object
interface PersonArgs {
    readonly name: string;
    readonly age: number;
}

// Cloneable abstract class initializes the object with super method and adds the clone method
// CloneableArgs interface ensures that all properties defined in the argument interface are defined in class
class Person extends Cloneable<TestArgs>  implements CloneableArgs<PersonArgs> {
    readonly name: string;
    readonly age: number;

    constructor(args: TestArgs) {
        super(args);
    }
}

const a = new Person({name: 'Alice', age: 28});
const b = a.clone({name: 'Bob'})
a.name // Alice
b.name // Bob
b.age // 28

或者你可以直接用克隆。克隆助手方法:

import {Cloneable} from 'cloneable-ts';

interface Person {
    readonly name: string;
    readonly age: number;
}

const a: Person = {name: 'Alice', age: 28};
const b = Cloneable.clone(a, {name: 'Bob'})
a.name // Alice
b.name // Bob
b.age // 28    

我的看法是:

Object.assign(…)只复制属性,我们丢失了原型和方法。

Object.create(…)不是为我复制属性,只是创建一个原型。

对我有用的是使用Object.create(…)创建一个原型,并使用Object.assign(…)将属性复制到它:

因此对于对象foo,像这样进行克隆:

Object.assign(Object.create(foo), foo)

使用扩展运算符… Const obj1 ={参数:"value"}; Const obj2 ={…其中obj1};

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

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

Object.assign () Const obj1={参数:"value"}; const obj2:any =对象。分配({},其中obj1);

对象。赋值创建真实的副本,但只有自己的属性,因此原型中的属性将不存在于被复制的对象中。这也是浅拷贝。


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

对象。Create不是做真正的克隆,它是从原型创建对象。因此,如果对象应该克隆主类型属性,请使用它,因为主类型属性的赋值不是通过引用完成的。

对象的优点。Create是指在prototype中声明的任何函数都可以在我们新创建的对象中使用。


关于浅复制的一些事情

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

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


深拷贝

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

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

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

如果还想复制方法,而不仅仅是复制数据,请遵循此方法

let copy = new BaseLayer() ;
Object.assign(copy, origin);
copy.x = 8 ; //will not affect the origin object

只需将BaseLayer更改为类的名称。