2
    Board = function()
    {
        var  cells = [8];


        /**
         * Initializing every cell using numeric format.
         * */
        for (var i=0 ; i<8; i++){
            cells[i] = [8];
            for (var j=0 ; j<8; j++)
                cells[i][j] = new Cell(new Position(i,j));
        }

                ....
}

In Another code GameManager.js,

var duplicateBoard = Copy.deepCopy(board);
board.moveCell(1,2)

And for Deepcopying I am using,

Ref : http://jsperf.com/deep-copy-vs-json-stringify-json-parse

 function deepCopy(o) {
        var copy = o,k;

        if (o && typeof o === 'object') {
            copy = Object.prototype.toString.call(o) === '[object Array]' ? [] : {};
            for (k in o) {
                copy[k] = deepCopy(o[k]);
            }
        }

        return copy;
    }

My need :
I want cells (private member of constructor ) in Board to be deep-copied.

Problem :
But, When I debugged with firebug, I saw, deepCopy function does not deep copying private objects of constructor.

My Case :
board.moveCell(1,2), Here cell[1][2] is moved in duplicateBoard too.
That is, No deep-copying of cell has taken place Both the board and duplicateBoard has same reference to cell[1][2].

What I have traced ? The deep-copy function, treats the constructor to be a function, hence it ignores deep-copying the functions, since it will fail in typeof o === 'object. But removing this condition is not useful, because by doing so, duplicateBoard has no functions rather all the functions to be object{} type.

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
Muthu Ganapathy Nathan
  • 3,199
  • 16
  • 47
  • 77
  • Can you use jQuery? If you can, maybe you'd try `copy = $.extend(true, {}, board);`, but Idk if it really works(not tested) – Niccolò Campolungo May 26 '13 at 14:39
  • 1
    The correct name for the "private" property is closure and they don't show up as object instance properties because they can only be used in the function body. How do you create Board instance(s), do you return a value or do you use the new keyword? – HMR May 26 '13 at 14:39
  • 1
    @LightStyle I don't think jQuery can copy properties defined in closure. Closure values are not part of the object instance and even within the object you cannot refer to them as this.cells – HMR May 26 '13 at 14:40
  • @HMR I used new keyword. Like new Board; Also, I made `cells` to be closure because, it cant be accessed by any other function – Muthu Ganapathy Nathan May 26 '13 at 14:41
  • @HMR ya my need is to maintain `cells` to be in closure (private), but still deep-copying them. – Muthu Ganapathy Nathan May 26 '13 at 14:44
  • @EAGER_STUDENT I'm quite sure you can't copy closure values, you might want to re considor making it private if you want to clone the object. – HMR May 26 '13 at 14:45
  • @HMR ya I need it remain `private` to ensure it maynot get accessed by other classes (DATA ABSTRACTION) – Muthu Ganapathy Nathan May 26 '13 at 14:46
  • A very dirty solution could be to create this.clone in the Board function (functions should be specified as prototype because they don't change for instances). Then create a new object in clone and return that. – HMR May 26 '13 at 14:49
  • @HMR Cloning means, how do I clone(deep-copy) the existing object in `this.clone`, there too the same problem will arise na. could you provide some sample code. – Muthu Ganapathy Nathan May 26 '13 at 14:53
  • 2
    the interesting dilemma of how can an external function copy the private variables of an object if it isn't supposed to be able to access them in the first place :-) – go-oleg May 26 '13 at 14:57
  • @user2359560 I can access the private members, by having `deep-Copy` in every function, which helps copying its members. – Muthu Ganapathy Nathan May 26 '13 at 15:08
  • 1
    @EAGER_STUDENT, what about [something like this](http://jsfiddle.net/dvnX3/)? – Alexander May 26 '13 at 23:56
  • I've updated my code, it'll copy the properties and their values including the closure variables. It breaks prototype of cloned instances but since you can't use prototype (you can't access closure vars in prototype) it would not matter. – HMR May 27 '13 at 01:38
  • @Alexander excellent, only thing left to do is copy other values in the clone function as currently it sets all the other values to default constructor values. And do not copy function values because it'll mess up the closure reference. I've added it in my answer. – HMR May 27 '13 at 02:56

3 Answers3

2

This cannot be done as the "private" variable is local to the function(constructor). With the way that JS works, even by cloning the functions you will still get a pointer from the original object (http://jsfiddle.net/kBzQP/),

function deepCopy(o) {
    if(o == null || typeof(o) != 'object') {
        return o;
    }

    var newObj = new o.constructor();

    for(var key in o) {
        newObj[key] = deepCopy(o[key]);
    }

    return newObj;  
}

if you do not clone functions then you get a brand new set of private variables with all the public variables cloned (http://jsfiddle.net/kBzQP/3/).

function deepCopy(o) {

    if(o == null || typeof(o) != 'object') {
        return o;
    }

    var newObj = new o.constructor();

    for(var key in o) {
        if(typeof(o) != 'function') continue;
        newObj[key] = deepCopy(o[key]);
    }

    return newObj;  
}

The best way to handle this is to make your private variables publicly accessible but give them a different naming convention such as "_myPrivateVariable". This way the variables will be cloned and anyone else using your class will know that this is a private variable.

So in your case it would be:

Board = function()
    {
        this._cells = [8];


        /**
         * Initializing every cell using numeric format.
         * */
        for (var i=0 ; i<8; i++){
            this._cells[i] = [8];
            for (var j=0 ; j<8; j++)
                this._cells[i][j] = new Cell(new Position(i,j));
        }

                ....
}

For reference sake check here: Copy javascript object with private member

Community
  • 1
  • 1
Michael Coxon
  • 5,311
  • 1
  • 24
  • 51
  • 1
    That would not copy closure values, if cells in o has been changed between creating o and calling deepCopy the change is not reflected in the returned new object. – HMR May 26 '13 at 14:57
  • Have you tried changing the value of msg between creating "o" and cloning it? let's add a function this.changeMsg(newMsg){msg=newMsg); call that and then clone it. Add a function this.sayMsg and check "o" and the clone, I'm sure they will not be the same. – HMR May 26 '13 at 15:15
  • @EAGER_STUDENT ahhh I see whats happening there.. the function pointer is being copied across. Please see http://jsfiddle.net/kBzQP/2/ for where I have modifiedthe function to not copy over the newly defined function pointers. – Michael Coxon May 26 '13 at 15:48
  • @EAGER_STUDENT please disregard my above message, this does not work in all cases – Michael Coxon May 26 '13 at 15:49
  • @MichaelCoxon I agree on not using private/closures especially if you plan to create a lot of instances. But if you break the prototype on the clone you can copy the private/closure variables as well, see my updated answer. Not sure if there is a valid reason to do this though. The clone is technically not a clone because the prototype is not copied, only properties and their values. – HMR May 27 '13 at 01:45
1

It's not a good solution as all functions accessing your "private" cells variable have to declared as this.someFunction instead of Board.prototype so each Board instance will have their own funcions instead of sharing them.

Here is some sample code that would break prototype (c instanceof b is not true) but since you can't use prototype because you need to access closure variables in your functions that would not matter.

function Test(privates) { 
    var msg = [];
    if(privates!==undefined){
      msg=deepCopy(privates.msg,[]);
    }
    this.Message = function(newMsg) {
        if (newMsg) {
            msg.push(newMsg);
        } else {
            return msg;
        }
    }
    this.clone=function(){
      var orgMsg=msg
      var ret = function(){
        Test.call(this,{msg:orgMsg});
      }
      return deepCopy(this,new ret());
    }
}
// this does not set prototype correctly
function deepCopy(from,to) {
    if(from == null || typeof(from) != 'object') {
        return from;
    }
    for(var key in from) {
      // this.Message has closure ref to msg
      // you can't copy it because we've set a new
      // closure ref
      if(typeof from[key]!=="function"){
        to[key] = deepCopy(from[key]);
      }
    }
    return to;  
}

var b = new Test();
b.Message("Before cloning");
console.log("b message before cloning:",b.Message());
var c = b.clone();
console.log("c message after cloning:",c.Message());
b.Message("From BB after Clone");
console.log("c message after pushing new item in b:",c.Message());
c.Message("From CC after Clone");
console.log("b message after pushing new item in c:",b.Message());
console.log("c message after pushing new item in b:",c.Message());

[UPDATE]

Why this is a bad desing is because you can't declare your object methods as prototype:

Test.prototype.Message(){
 //here the msg variable doesn't exist
}

This forces you to declare all your methods in the Test body with "this.someFunction" syntax. If you create multiple Test instances than each instance has it's own set of methods doing the exact same thing. To save resources you should use prototype but then you can't access closure varibales in these funcitons so you can't. Please read this on prototype basics: Prototypical inheritance - writing up

Maybe if you only have a couple of instances it wouldn't matter but technically you can't clone these objects. A real clone of b in the above code would be typeof Test but in the code above cloned instance of "b" called "c" is not typeof Test and there is no way I can see setting it without breaking the newly set closure variable called "msg".

Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
  • How does `var cells=cells` helps in deep-copying? Also where is the deep-copying taking place? Since `Cells` will inturn contain `Position` so, recursion is needed in my case. sorry for disturbing you again. – Muthu Ganapathy Nathan May 26 '13 at 15:04
  • Setting a closure reference to cells of the current instance of Board "this" so the to be returned instance of Board "ret" has a reference to it. I'm not sure if it'll work and I would not keep cells private. As far as I know using closure prevents you from using prototype to extend your object because closure/privates are not accesable in prototype declared properties. Here is a basic explanation about prototype: http://stackoverflow.com/questions/16063394/prototypical-inheritance-writing-up/16063711#16063711 – HMR May 26 '13 at 15:10
  • @EAGER_STUDENT sorry, but I think the best solution would be to not use private closure values on objects like this. google.closure is a set of tools that built applications like gmail, you can have private properties in objects by declaring them with comments so the compiler complains but they are not actually implemented as private in JS because it would force you to make "bad" programming designs (you can't use prototype to declare function properties that use the private/closure cells variable). – HMR May 26 '13 at 15:21
  • So, Having `cell` to be a private variable is a bad design?. Or, `Deep-copying` closure is impossible? – Muthu Ganapathy Nathan May 26 '13 at 15:28
  • Just a note that cell is private to the Board constructor and can't even be referenced by functions defined on Board's prototype. – go-oleg May 26 '13 at 15:39
  • @EAGER_STUDENT Updated code, you could try something like that but if you're planning on using a lot of instances then please re considor using private variables as they are expensive in resources. – HMR May 27 '13 at 01:40
  • That's a pretty good idea, adding the clone function as a internal method. It will be able to access the private values. +1 – Michael Coxon May 27 '13 at 07:38
  • Thank you Michael, Alexander came up with the same clone function in a fiddle. It takes care of copying the closure variables. Then all the other public members need to be copied except functions because that makes it loose it's newly copied closure variable. – HMR May 27 '13 at 07:45
0

Use $.extend():

var testObj = function() {
    var rand = Math.random(0, 1);
    this.r = function() { 
        return rand; 
    };
    this.changeRand = function() {
        rand = Math.random(0, 1);
    };
};
var obj1 = new testObj();
alert(obj1.r());
obj1.changeRand();
alert(obj1.r());
var obj2 = $.extend(true, {}, obj1);
alert(obj2.r());
alert(obj1.r() === obj2.r()); // true

JSFiddle

In the same way you should use it for your board:

var Board = function() {
        var cells = [8];
        /**
         * Initializing every cell using numeric format.
         * */
        for (var i=0 ; i<8; i++){
            cells[i] = [8];
            for (var j=0 ; j<8; j++)
                cells[i][j] = new Cell(new Position(i,j));
        }
}
var board = new Board(),
    copy = $.extend(true, {}, board);

Normally I try to avoid using jQuery, but in this case it seems perfect...

Niccolò Campolungo
  • 11,824
  • 4
  • 32
  • 39
  • 1
    That would not copy the cells closure in the board item I think and if it did then it would copy changes to cells if cells were changed between creating board and cloning it. – HMR May 26 '13 at 14:59
  • See the new Fiddle. It changes the closure variable and saves it even in the cloned object. It works perfectly. @EAGER_STUDENT check it out! – Niccolò Campolungo May 26 '13 at 16:54
  • The cloned object and returned object share the same msg, try to change msg to an array and function Message to push the passed variable in msg, then clone it and call Message on either one of the objects to push a new value in the msg array. You will see that the other one has the same item pushed in in message. @LightStyle – HMR May 27 '13 at 00:23