Case Sandberg
Case Sandberg

Reputation: 180

Clone instance to independent object

I have a board class:

var board = new Board();

I want to make a clone of the board and run a movePiece function on the new board:

var newBoard = board;
newBoard.movePiece('4a', '3b');

Since I am just assigning it, both board and newBoard will have the moved piece.

How can I clone board to create an exact copy called newBoard that I can move the piece on and the original board will stay the same?


I have tried a couple different methods, such as:

Object.assign({}, orig)

and

function clone(orig) {
    let origProto = Object.getPrototypeOf(orig);
    return Object.assign(Object.create(origProto), orig);
}

from http://www.2ality.com/2014/12/es6-oop.html

Upvotes: 4

Views: 1315

Answers (1)

jfriend00
jfriend00

Reputation: 707148

The only way to make a reliable clone for an arbitrary object is to write a method on that object that takes one instance and creates a new instance from the properties of the original instance.

This is because there's no way for an outside agent to know exactly how to treat every instance variable (what needs to be assigned, what needs to be copied, etc...) and it's also possible to have private instance variables too. So, just write a new method that creates a new object and copies over relevant properties or a constructor that takes another object and does the same.

It is possible to have an object that one could just assign all the properties to a new instance (if all properties were primitives), but that is usually not the case so more intelligence needs to be applied as to how to best assign each property to the new clone. And, an object could have private instance variables (closure variables) that could have to be dealt with manually too from the within some other method.

Here's an example of a relatively simple object that just has an array in it that needs to be copied. This one supports creation of a clone either via the constructor or via a clone method:

function CallbackList(otherObj) {
    // if another object is passed to the constructor, then initialize this
    // new instance to be a copy
    if (otherObj) {
        // copy the array from the other instance
        this.list = otherObj.list.slice(0);
    } else {
        // initalize empty list
        this.list = [];
    }
}

CallbackList.prototype = {
    addListener: function(fn) {
        this.list.push(fn);
    },
    removeListener: function(fn) {
        for (var i = this.list.length - 1; i >= 0; i--) {
           if (this.list[i] === fn) {
              this.list.splice(i, 1);
           }
        }
    },
    fire: function(/* pass args here */) {
        var args = Array.prototype.slice.call(arguments);
        this.list.forEach(function(fn) {
            fn.apply(null, args);
        });
    },
    clone: function() {
        return new CallbackList(this);
    }
};

To understand why you need custom code that is built specifically for your object to create a clone, here are some of the things that could be in the instance data that you may have to treat in an object-specific way:

  1. A reference to a DOM object. Maybe, the clone should have the same reference (just copied) or maybe a new DOM object needs to be created and inserted into the DOM.
  2. References to other objects. Maybe the new clone should have the same reference or maybe a new object needs to be created for the clone.
  3. Array. If it's a reference to a shared array, then both clones should point to the same array. If it's something that is supposed to be a unique piece of instance data that is separate for each object, then the array needs to be copied. If the array itself contains objects, then you have the same questions for the contents of the array too.
  4. Object. Same issues as an array.
  5. Closure variables (instance variables that are private). Same issues as mentioned above with object properties.

Upvotes: 6

Related Questions