0

I'm trying to create an ES6 class that has its own clone method allowing itself to be cloned. This should create a new instance of the class, and copy over all of its public data to the new instance. I'm implementing the class like so:

class TestCloneable {
    constructor(someVal) {
        if (someVal) { this.someVal = someVal; }
    }

    clone = () => {
        const cloned = new TestCloneable(this.someVal);
        Object.assign(cloned, this); // same behaviour for Object.assign(cloned, {...this});
        return cloned;
    };
}

let test1 = new TestCloneable(1);
test1.anotherVal = 2;
let test2 = test1.clone();
test2.anotherVal = 3;
let test3 = test2.clone();

console.log(test1);
console.log(test2);
console.log(test3);

What I don't understand is why test3.anotherVal is 2. I set it to 3 on test2, then cloned test2. Somehow my usage of Object.assign seems to be causing this to refer back to test1 within the clone method of test2 instead of refering to test2 itself. How can I fix this?

1 Answer 1

1

That's because this syntax clone = () => { } doesn't create a method, it creates a property

Use correct syntax and it will work

class TestCloneable {
    constructor(someVal) {
        if (someVal) { this.someVal = someVal; }
    }

    clone() {
        const cloned = new TestCloneable(this.someVal);
        Object.assign(cloned, this);
        return cloned;
    };
}

let test1 = new TestCloneable(1);
test1.anotherVal = 2;
let test2 = test1.clone();
test2.anotherVal = 3;
let test3 = test2.clone();

console.log(test1);
console.log(test2);
console.log(test3);

When you used Object.assign you also copied the clone method including the this context. So next time you run clone this is still pointing to the first object.

Cloning everything except the clone function results in the expected behaviour:

class TestCloneable {
    constructor(someVal) {
        if (someVal) { this.someVal = someVal; }
    }

    clone = () => {
        const cloned = new TestCloneable(this.someVal);
        cloned.anotherVal = this.anotherVal;
        cloned.someVal = this.someVal;
        return cloned;
    };
}

let test1 = new TestCloneable(1);
test1.anotherVal = 2;
let test2 = test1.clone();
test2.anotherVal = 3;
let test3 = test2.clone();

console.log(test1);
console.log(test2);
console.log(test3);

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

4 Comments

But shouldn't an arrow function's this resolve to the containing object instance? I thought that was how it worked.
When you used Object.assign you also copied the clone method including the this context. So next time you run clone this is still pointing to the first object.
Right. Would an alternative be to filter out all functions from {...this} before passing it to Object.assign?
You could do that but I would just stick to a method syntax.

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.