如何在深度复制时深层复制闭包对象(私有对象)

时间:2013-05-26 14:34:31

标签: javascript clone

    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));
        }

                ....
}

在另一个代码GameManager.js中,

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

对于我正在使用的Deepcopying,

参考: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;
    }

我的需要:
        我希望cells中的Board(构造函数的私有成员)被深层复制。

问题:
但是,当我用firebug调试时,我看到,deepCopy函数没有深度复制构造函数的私有对象。

我的案例:
board.moveCell(1,2),此处单元格[1] [2]也在duplicateBoard中移动。
那是, 没有对cell进行深度复制 board和duplicateBoard都对cell [1] [2]有相同的引用。

我追踪的是什么?  深拷贝函数将constructor视为函数,因此它忽略了对函数的深度复制,因为它将在typeof o === 'object中失败。但是删除这个条件没有用,因为通过这样做,duplicateBoard没有函数,而是所有函数都是object{}类型。

3 个答案:

答案 0 :(得分:2)

这不能完成,因为“private”变量是函数(构造函数)的本地变量。通过JS的工作方式,即使通过克隆函数,您仍然可以从原始对象(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;  
}

如果您不克隆函数,那么您将获得一组全新的私有变量,其中包含克隆的所有公共变量(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;  
}

处理此问题的最佳方法是使您的私有变量可公开访问,但为其提供不同的命名约定,例如“_myPrivateVariable”。这样就可以克隆变量,使用你的类的任何人都知道这是一个私有变量。

所以在你的情况下,它将是:

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));
        }

                ....
}

为了参考,请点击此处:Copy javascript object with private member

答案 1 :(得分:1)

这不是一个好的解决方案,因为访问“私有”单元变量的所有函数都必须声明为this.someFunction而不是Board.prototype,因此每个Board实例都有自己的功能而不是共享它们。

下面是一些破坏原型的示例代码(c instanceof b不是真的),但由于你不能使用原型,因为你需要在你的函数中访问无关紧要的闭包变量。

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]

为什么这是一个糟糕的设计是因为你不能将你的对象方法声明为原型:

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

这会强制您使用“this.someFunction”语法在Test主体中声明所有方法。如果您创建多个Test实例,那么每个实例都有自己的一组方法执行完全相同的操作。为了节省资源,你应该使用原型,但是你不能在这些函数中访问闭包变量,所以你不能。请在原型基础知识上阅读:Prototypical inheritance - writing up

也许如果你只有几个实例,那就无所谓了,但从技术上讲,你无法克隆这些对象。上面代码中b的真正克隆将是typeof Test但是在上面的代码中,被称为“c”的克隆实例“b”不是testof类型,我无法在不破坏新设置的闭包变量的情况下看到它的设置“msg” 中。

答案 2 :(得分:0)

使用$.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

以同样的方式将它用于您的电路板:

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);

通常我会尽量避免使用jQuery,但在这种情况下它看起来很完美......

相关问题