Javascript 打字稿 - 克隆对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28150967/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 01:17:23  来源:igfitidea点击:

typescript - cloning object

javascripttypescript

提问by David Laberge

I have a super class that is the parent (Entity) for many subclass (Customer, Product, ProductCategory...)

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

I'm looking to clone dynamically an object that contains different sub objects in Typescript.

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

In example : a Customerthat has different Productwho has a ProductCategory

例如: aCustomer有不同的Productwho 有ProductCategory

var cust:Customer  = new Customer ();

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

In order to clone the whole tree of object I created a function in Entity

为了克隆整个对象树,我在 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;
}

The newrises the following error when it is transpiled to javascript: error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.

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

Although the script works, I would like to get rid of the transpiled error

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

回答by Fenton

Solving The Specific Issue

解决具体问题

You can use a type assertion to tell the compiler that you know better:

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

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

Cloning

克隆

Bear in mind that sometimes it is better to write your own mapping - rather than being totally dynamic. However, there are a few "cloning" tricks you can use that give you difference effects.

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

I will use the following code for all the subsequent examples:

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

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'));


Option 1: Spread

选项 1:传播

Properties: Yes
Methods: No
Deep Copy: No

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

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


Option 2: Object.assign

选项 2:Object.assign

Properties: Yes
Methods: No
Deep Copy: No

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

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


Option 3: Object.create

选项 3:Object.create

Properties: Inherited
Methods: Inherited
Deep Copy: Shallow Inherited(deep changes affect both original and clone)

属性:继承
方法:继承
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


Option 4: Deep Copy Function

选项 4:深度复制功能

Properties: Yes
Methods: No
Deep Copy: Yes

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

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

回答by Maciej Sikora

1.Use spread operator

1.使用扩展运算符

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

Spread operator takes all fields from obj1 and spread them over obj2. In the result you get new object with new reference and the same fields as original one.

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

Remember that it is shallow copy, it means that if object is nested then its nested composite params will exists in the new object by the same reference.

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

2.Object.assign()

2.Object.assign()

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

Object.assigncreate real copy, but only own properties, so properties in prototype will not exist in copied object. It is also shallow copy.

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



3.Object.create()

3.Object.create()

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

Object.createis not doing real cloning, it is creating object from prototype. So use it if the object should clone primary type properties, because primary type properties assignment is not done by reference.

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

Pluses of Object.createare that any functions declared in prototype will be available in our newly created object.

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



Few things about shallow copy

关于浅拷贝的几件事

Shallow copy puts into new object all fields of the old one, but it also means that if original object has composite type fields (object, arrays etc.) then those fields are put in new object with the same references. Mutation such field in original object will be reflected in new object.

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

It maybe looks like a pitfall, but really situation when the whole complex object needs to be copied is rare. Shallow copy will re-use most of memory which means that is very cheap in comparison to deep copy.

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



Deep copy

深拷贝

Spread operator can be handy for deep copy.

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

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

Above code created deep copy of obj1. Composite field "complex" was also copied into obj2. Mutation field "complex" will not reflect the copy.

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

回答by Lars

Try this:

尝试这个:

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

It is a good solution until you are using very large objects or your object has unserializable properties.

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

In order to preserve type safety you could use a copy function in the class you want to make copies from:

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

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

or in a static way:

或以静态方式:

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

回答by Luca C.

Typescript/Javascript has its own operator for shallow cloning:

Typescript/Javascript 有自己的浅层克隆操作符:

let shallowClone = { ...original };

回答by Homer

It's easy to get a shallow copy with "Object Spread" introduced in TypeScript 2.1

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

this TypeScript: let copy = { ...original };

这个打字稿: let copy = { ...original };

produces this JavaScript:

产生这个 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

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

回答by Polv

For serializable deep clone, with Type Information is,

对于可序列化的深度克隆,类型信息是,

export function clone<T>(a: T): T {
  return JSON.parse(JSON.stringify(a));
}

回答by Muhammad Ali

My take on it:

我的看法:

Object.assign(...)only copies properties and we lose the prototype and methods.

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

Object.create(...)is not copying properties for me and just creating a prototype.

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

What worked for me is creating a prototype using Object.create(...)and copying properties to it using Object.assign(...):

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

So for an object foo, make clone like this:

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

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

回答by Decade Moon

You can also have something like this:

你也可以有这样的事情:

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

Just make sure that you override the clonemethod in all Entitysubclasses otherwise you'll end up with partial clones.

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

The return type of thiswill always match the type of the instance.

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

回答by pablorsk

If you get this error:

如果您收到此错误:

TypeError: this.constructor(...) is not a function

This is the correct script:

这是正确的脚本:

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

回答by Tim Osadchiy

Came across this problem myself and in the end wrote a small library cloneable-tsthat provides an abstract class, which adds a clone method to any class extending it. The abstract class borrows the Deep Copy Function described in the accepted answer by Fenton only replacing copy = {};with copy = Object.create(originalObj)to preserve the class of the original object. Here is an example of using the class.

自己遇到了这个问题,最后写了一个小库cloneable-ts,它提供了一个抽象类,它为任何扩展它的类添加了一个 clone 方法。抽象类借用 Fenton 接受的答案中描述的 Deep Copy Function 仅替换copy = {};withcopy = 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

Or you could just use the Cloneable.clonehelper method:

或者你可以只使用Cloneable.clone辅助方法:

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