0

I have two objects inst1, inst2 which are both instances of the same class. If I use

inst2 = JSON.parse(JSON.stringify(inst1));

now if I change values of properties of inst2, values in inst1 do not change. That is great. But sadly methods of inst2 have disappeared. So if I do

inst2.method1();

I get the error "inst2.method1 is not a function"

Is there some way I can copy the values in an instance without destroying methods? (obviously I could laboriously copy each value. I am trying to avoid that because I am lazy.)

I have tried to follow typescript - cloning object but I cannot make it work-

2
  • Does this answer your question? typescript - cloning object Commented Nov 18, 2022 at 11:19
  • It looks good but I can't make it work. Commented Nov 18, 2022 at 15:17

3 Answers 3

1

Ok, I have played a little since the provided answers are not 100% clear. If you want to have a shallow copy and copy the methods too, you can use Object.create.

Again: If your object is simple enough, Object.create will be sufficient for you

const originalPerson = new Person("John");
originalPerson.address = new Address("Paris", "France");


const newPerson = Object.create(originalPerson);

/// this will be true
const isInstanceOf = newPerson instanceof Person;

//this will change the property of the new person ONLY
newPerson.name = "Peter";

//methods will work
newPerson.someMethod();

//methods will work even on nested objects instances
newPerson.address.anotherMethod();

// BUT if we change the city on any of the instances -  will change the address.city of both persons since we have done a shallow copy
newPerson.address.city = "Berlin";

I have created typescript playground (just remove the types) to show it works and the drawback with its usage - link to the playground

Another approach is the class itself to have a clone method and to be responsible for its own cloning logic. An example follows, along with a link to another playground

class Address {
  constructor(city, country) {
    this.city = city;
    this.country = country;
  }

  clone() {
    // no special logic, BUT if the address eveolves this is the place to change the clone behvaiour
    return Object.create(this);
  }

  getAddressDetails() {
    return `City: ${this.city} country ${this.country}`;
  }
}

class Person {
  constructor(name, address) {
    this.name = name;
    this.address = address;
  }

  clone() {
    const newInstance = Object.create(this);
    //clone all other class instances
    newInstance.address = this.address.clone();
    return newInstance;
  }

  getPersonDetails() {
    //calling internally address.getAddressDetails() ensures that the inner object methods are also cloned
    return `This is ${this.name}, I live in ${this.address.getAddressDetails()}`
  }
}

const originalAddress = new Address("Paris", "France");
const originalPerson = new Person("John", originalAddress);

const clonedPerson = originalPerson.clone();
clonedPerson.name = "Peter";
clonedPerson.address.city = "Berlin";
clonedPerson.address.country = "Germany";

// Log to console
console.log(`Original person: ${originalPerson.getPersonDetails()}`)
console.log(`Cloned person: ${clonedPerson.getPersonDetails()}`)

Sign up to request clarification or add additional context in comments.

Comments

0

You should use structured cloning, see this answer: How do I correctly clone a JavaScript object?

The reason that your current code isn't working is because you are parsing a stringified json object. Json stringify will remove all of the methods of an object and only stringify the objects values.

1 Comment

I tried stackoverflow.com/questions/728360/… too. The object returned was no longer an instance of the class of the input object. So the methods had gone again. Thanks anyway.
0

I came back to this at a convenient point and made quite a bit of progress by combining some of the above answers. The general purpose cloner was getting quite ugly (see below) and still not working (for arrays of class-objects) when I realised that it would be impossible to write a general purpose cloner.

I use the term class-object to mean an object defined by a class.

If a class-object contains a variable which itself is type class-object, call it subObj, then the general purpose cloner cannot know whether 1) it should copy subObj or 2) it should create a new instance of subObj and copy into the sub-properties. The answer depends on the meaning in the class.

In the first case above subObj. is just a pointer to another instance of subObj.

Therefore I strongly agree with the second part of Svetoslav Petkov's answer that the "class itself [should] have a clone method and be responsible for its own cloning logic.".

For what it's worth this is as far as I got with a general purpose cloner (in TypeScript). It is adapted from the other answers and creates new instances of class-objects liberally:

public clone(): any {
var cloneObj = new (this.constructor as any)() as any;
for (var attribut in this) {
  // attribut is a string which will take the values of the names of the propertirs in 'this'
  // And for example, if aNumber is a property of 'this' then
  // this['aNumber'] is the same as this.aNumber
  if (typeof this[attribut] === "object") {
    let thisAttr = this[attribut] as any;
    let cloneAttr = cloneObj[attribut] as any;
    if (this[attribut] instanceof Array) {
      for (let i in thisAttr) {
        cloneAttr[i] = thisAttr[i];         // **** will not work on arrays of objects!!
      }
      continue; // to next attrib in this
    }
    if (this[attribut] instanceof Date) {
      cloneAttr.setTime(thisAttr.getTime());
      continue; // to next attrib in this
    }
    try {
      cloneObj[attribut] = thisAttr.clone();
      //cloneObj[attribut] = this.clone();     // with this, (from https://stackoverflow.com/questions/28150967/typescript-cloning-object) stack just gets bigger until overflow
    }
    catch (err) {
      alert("Error: Object " + attribut + " does not have clone method." +
        "\nOr " + err.message);
    }
  } else {
    cloneObj[attribut] = this[attribut];
  }
}
return cloneObj;

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.