typescript - 克隆对象解决具体问题克隆

2022-08-29 23:57:30

我有一个超类,它是许多子类(,,...)的父类()EntityCustomerProductProductCategory

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

例如:a 具有不同谁有CustomerProductProductCategory

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;
}

当它被转译为javascript时,它会上升以下错误:newerror TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.

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


答案 1

解决具体问题

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

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;
}

克隆

截至2022年,有一项建议允许深度复制许多类型。structuredClone

const copy = structuredClone(value)

对于您可以在哪种事情上使用它,有一些限制。

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

我将在下面的代码中用于所有后续示例:

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:对象分配

属性:
方法:无
深度复制:否

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:对象创建

属性:继承
的方法:继承的
深层复制:浅层继承(深层更改会影响原始副本和克隆副本)

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

答案 2
  1. 使用点差运算符...

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

传播运算符从 obj1 中获取所有字段,并将它们分布在 obj2 上。在结果中,您将获得具有新引用的新对象以及与原始对象相同的字段。

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

  1. Object.assign()

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

Object.assign 创建真实副本,但仅创建自己的属性,因此原型中的属性将不存在于复制的对象中。它也是浅拷贝。


  1. Object.create()

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

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

Object.create的优点是,prototype中声明的任何函数都将在我们新创建的对象中可用。


关于浅拷贝的几件事

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

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


深度复制

点差运算符可以方便地进行深度复制。

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

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