2

I have a Point

function Point(x, y) {
    this.x = x;
    this.y = y;
};

As you see, it's mutable. So I can change it properties, like

 var p = new Point(2, 3);
 p.x = 6;

I want to add clone method so that expected behavior would be

 var p1 = new Point(2, 3);
 var p2 = p1.clone();
 p1.x = 6;

 assert p1 != p2;     //first assertion. pseudocode.
 assert p2.x == 2;    //second assertion. pseudocode.

For implementing clone() I rewrite Point in next way

function Point(x, y) {
    this.x = x;
    this.y = y;
    this.clone = function () {
        function TrickyConstructor() {
        }
        TrickyConstructor.prototype = this;
        return new TrickyConstructor();
    };
};

But second assertion fails for my implementation. How should I reimplement it?

Stan Kurilin
  • 15,614
  • 21
  • 81
  • 132
  • possible duplicate of [How to Deep clone in javascript](http://stackoverflow.com/questions/4459928/how-to-deep-clone-in-javascript) – Felix Kling Jul 08 '11 at 11:07

2 Answers2

4

If the properties are only x and y, I would do this:

function Point(x, y) {
    this.x = x;
    this.y = y;
};

Point.prototype.clone = function() {
    return new Point(this.x, this.y);
}

Note that I attach the clone method to Point.prototype. This is important for the next method to work:

If not, you would have to create a new instance and maybe copy all properties to the new instance:

Point.prototype.clone = function() {
    var clone = new Point(this.x, this.y);
    for(var prop in this) {
        if(this.hasOwnProperty(prop)) {
            clone[prop] = this[prop];
        }
    }
    return clone;
}

but this will not deep copy properties. This only works for primitive values.

If you really want to deep copy properties, this can get much more complex. Luckily, this has already been asked before: How to Deep clone in javascript


Explanation of why your clone method does not work:

The prototype chain of p2 will look like this:

 +-----------+      +-----------+
 |Instance p2|      |Instance p1|
 |           |----->|x=2        |
 |           |      |y=3        |
 +-----------+      +-----------+

so if you set p1.x = 6 it will be:

 +-----------+      +-----------+
 |Instance p2|      |Instance p1|
 |           |----->|x=6        |
 |           |      |y=3        |
 +-----------+      +-----------+

As long as p2 has no own x or y properties, they will always refer to the ones of the prototype which happens to be p1.

Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • Thanks, but actually this question about general technique, not just solving this particular issue. – Stan Kurilin Jul 08 '11 at 11:04
  • @StasKurilin: Then this question was already answered: http://stackoverflow.com/questions/4459928/how-to-deep-clone-in-javascript – Felix Kling Jul 08 '11 at 11:07
1
function Point(x, y) {
    this.x = x;
    this.y = y;
    this.clone = function () {
        var newPoint = {};
        for (var key in this) {
            newPoint[key] = this[key];
        }
        return newPoint;
    };
};

Example: http://jsfiddle.net/HPtmk/

aroth
  • 54,026
  • 20
  • 135
  • 176
  • Toss in a *if (typeof this[key] == 'function') continue;* No need to clone methods. – Wolfgang Kuehn Jul 08 '11 at 11:09
  • @amadeus - It's not cloning the fields, just assigning (and the assignment is necessary in this case, because `newPoint` does not start as a `Point` instance). It behaves as a deep-copy in this case because the source object only has two primitive fields. To do a proper deep copy it would need to check for `Object` and `Array` types and recurse through them. – aroth Jul 08 '11 at 11:17
  • You are right. But why would you start from a blank object. Wouldn't that be a waste of space if you had many Points each with many methods? Why can't I insert @aroth at the beginning of the comment? – Wolfgang Kuehn Jul 08 '11 at 11:44
  • @amadeus - Starting from the blank object uses no more space than starting from a `Point`. The `clone` method is not copied, just the reference to it. The same exact thing happens if you do `new Point(x, y)`, the new instance gets a copy of the reference to the original definition of `clone()`. You can see this in the fiddle by testing if `p1.clone == p2.clone`, as shown here: http://jsfiddle.net/HPtmk/1 – aroth Jul 08 '11 at 11:52
  • @amadeus: Regarding the @ see here: http://meta.stackexchange.com/questions/59445/recent-feature-changes-to-stack-exchange/59446#59446 (2011-07-01) – Felix Kling Jul 08 '11 at 13:24