javascript – typescript – cloning object

javascript – typescript – cloning object

Solving The Specific Issue

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

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

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

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

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 wont spot it

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

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

Option 3: Object.create

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

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

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 isnt 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

1.Use spread operator

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.

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()

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

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


3.Object.create()

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

Object.create is 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.

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


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.

javascript – typescript – cloning object

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

Leave a Reply

Your email address will not be published.