打字稿 - 克隆对象

Dav*_*rge 131 javascript typescript

我有一个超类是父(Entity)对于很多子类(Customer,Product,ProductCategory...)

我正在寻找动态克隆在Typescript中包含不同子对象的对象.

例如:a Customer有不同的Product人有ProductCategory

var cust:Customer  = new Customer ();

cust.name = "someName";
cust.products.push(new Product(someId1));
cust.products.push(new Product(someId2));
Run Code Online (Sandbox Code Playgroud)

为了克隆整个对象树,我创建了一个函数 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;
}
Run Code Online (Sandbox Code Playgroud)

new上升时,它被transpiled为JavaScript以下错误:error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.

虽然脚本有效,但我想摆脱已经发现的错误

Fen*_*ton 188

解决具体问题

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

public clone(): any {
    var cloneObj = new (<any>this.constructor());
    for (var attribut in this) {
        if (typeof this[attribut] === "object") {
            cloneObj[attribut] = this[attribut].clone();
        } else {
            cloneObj[attribut] = this[attribut];
        }
    }
    return cloneObj;
}
Run Code Online (Sandbox Code Playgroud)

克隆

请记住,有时候编写自己的映射会更好 - 而不是完全动态.但是,你可以使用一些"克隆"技巧来给你带来不同的效果.

我将对以后的所有示例使用以下代码:

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'));
Run Code Online (Sandbox Code Playgroud)

选项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
Run Code Online (Sandbox Code Playgroud)

选项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
Run Code Online (Sandbox Code Playgroud)

选项3:Object.create

属性:
方法:
深拷贝:否

var clone = Object.create(customer);

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

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

alert(customer.name + ' ' + customer.example.type); // David SteveType
Run Code Online (Sandbox Code Playgroud)

选项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 = <Customer>deepCopy(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
Run Code Online (Sandbox Code Playgroud)

  • 您是否只是从该客户中做出公开榜样? (2认同)

Mac*_*ora 155

1.使用传播运营商

const obj1 = { param: "value" };
const obj2 = { ...obj1 };
Run Code Online (Sandbox Code Playgroud)

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

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

2.Object.assign()

const obj1={ param: "value" };
const obj2:any = Object.assign({}, obj1);
Run Code Online (Sandbox Code Playgroud)

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


3.Object.create()

const obj1={ param: "value" };
const obj2:any = Object.create(obj1);
Run Code Online (Sandbox Code Playgroud)

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

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


关于浅拷贝的一些事情

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

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


深拷贝

Spread运算符可以方便深度复制.

const obj1 = { param: "value", complex: { name: "John"}}
const obj2 = { ...obj1, complex: {...obj1.complex}};
Run Code Online (Sandbox Code Playgroud)

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

  • 我认为这不完全正确.`Object.create(obj1)`创建一个新对象并将obj1指定为原型.obj1中的所有字段都不会被复制或克隆.因此,可以看到obj1的更改而不修改obj2,因为它基本上没有属性.如果首先修改obj2,则不会在您定义的字段中看到原型,因为obj2的名称字段在层次结构中更接近. (6认同)
  • Object.assign将为深层对象创建问题.例如{name:'x',values:['a','b','c']}.使用Object.assign进行克隆后,两个对象共享values数组,因此更新一个会影响另一个.请参阅:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign("深度克隆警告"部分).它说:对于深度克隆,我们需要使用其他替代方案.这是因为当被分配的属性是对象时,Object.assign()会复制属性引用. (5认同)
  • 您还将看到ES2015和打字稿开发人员这样做,它从第一个参数创建一个对象(在我的例子中是一个空的)并从第二个和后续的参数复制属性):`let b = Object.assign({ },a);` (2认同)

Lar*_*ars 38

试试这个:

let copy = (JSON.parse(JSON.stringify(objectToCopy)));
Run Code Online (Sandbox Code Playgroud)

在使用非常大的对象或对象具有不可序列化的属性之前,这是一个很好的解决方案.

为了保护类型安全,您可以在要从中复制的类中使用复制功能:

getCopy(): YourClassName{
    return (JSON.parse(JSON.stringify(this)));
}
Run Code Online (Sandbox Code Playgroud)

或以静态方式:

static createCopy(objectToCopy: YourClassName): YourClassName{
    return (JSON.parse(JSON.stringify(objectToCopy)));
}
Run Code Online (Sandbox Code Playgroud)

  • 可以,但是请记住,序列化/解析时会丢失原型信息以及json中不支持的所有类型。 (2认同)

Luc*_* C. 28

Typescript/Javascript有自己的浅层克隆运算符:

let shallowClone = { ...original };
Run Code Online (Sandbox Code Playgroud)

  • 任何使用此功能的人请注意,如果存在嵌套对象,则引用将被保留,因此是“浅”的。 (3认同)

Hom*_*mer 14

使用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);
Run Code Online (Sandbox Code Playgroud)

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

  • 注意:这将创建一个浅表副本 (2认同)

San*_*dze 11

这是deepCopyTypeScript 中的实现(any代码中未包含):

const deepCopy = <T, U = T extends Array<infer V> ? V : never>(source: T ): T => {
  if (Array.isArray(source)) {
    return source.map(item => (deepCopy(item))) as T & U[]
  }
  if (source instanceof Date) {
    return new Date(source.getTime()) as T & Date
  }
  if (source && typeof source === 'object') {
    return (Object.getOwnPropertyNames(source) as (keyof T)[]).reduce<T>((o, prop) => {
      Object.defineProperty(o, prop, Object.getOwnPropertyDescriptor(source, prop)!)
      o[prop] = deepCopy(source[prop])
      return o
    }, Object.create(Object.getPrototypeOf(source)))
  }
  return source
}
Run Code Online (Sandbox Code Playgroud)


Pol*_*olv 9

对于可序列化的深层克隆,其类型信息为:

export function clone<T>(a: T): T {
  return JSON.parse(JSON.stringify(a));
}
Run Code Online (Sandbox Code Playgroud)

  • 这可以改变道具的顺序。只是对某些人的警告。它也不能正确处理日期。 (3认同)
  • @Polv,如果有人依赖于对象中键的顺序,我认为他们有比“克隆”更大的问题。:) (3认同)

Muh*_*Ali 8

我的看法:

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

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

对我有用的是使用以下方法创建原型Object.create(...)并将属性复制到它Object.assign(...)

所以对于一个对象foo,像这样进行克隆:

Object.assign(Object.create(foo), foo)
Run Code Online (Sandbox Code Playgroud)


Mau*_*rez 7

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

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

只需更改BaseLayer为您的班级名称即可。


use*_*850 6

添加"lodash.clonedeep": "^4.5.0"到您的package.json. 然后像这样使用:

import * as _ from 'lodash';

...

const copy = _.cloneDeep(original)
Run Code Online (Sandbox Code Playgroud)


Dec*_*oon 5

您也可以有以下内容:

class Entity {
    id: number;

    constructor(id: number) {
        this.id = id;
    }

    clone(): this {
        return new (this.constructor as typeof Entity)(this.id) as this;
    }
}

class Customer extends Entity {
    name: string;

    constructor(id: number, name: string) {
        super(id);
        this.name = name;
    }

    clone(): this {
        return new (this.constructor as typeof Customer)(this.id, this.name) as this;
    }
}
Run Code Online (Sandbox Code Playgroud)

只需确保clone在所有Entity子类中覆盖该方法,否则最终将得到部分克隆。

的返回类型this将始终与实例的类型匹配。