Stanislav Müller
Stanislav Müller

Reputation: 439

Cloning JavaScript Objects. Again :(

I know it is really annoying to read this topic again. Before you start diggin into the code, one solution could be that I don't get prototypes and objects in JavaScript. But at this point i think, i do.

The problem is:
How to clone an JavaScript Class (created with prototypes), so that the “cloned” Class remains untouched when extending and executing afterwards?

function clone(obj){
  if(obj == null || typeof(obj) != 'object')
      return obj;

  var temp = new obj.constructor();
  for(var key in obj)
      temp[key] = clone(obj[key]);

  return temp;
}

var FOO = function() {
  var myBAR = clone(BAR);
  myBAR.prototype = jQuery.extend(true, myBAR.prototype, this); // deep cloning twice and extending with this
  console.log("FOO:", this.name);
  new myBAR();
};

FOO.prototype = {
  name: "FOO"
};

var BAR = function() {
  console.log("BAR:", this.name);
};

BAR.prototype = {
  name: "BAR"
};

new FOO(); // returns FOO: FOO and BAR: FOO 
new BAR(); // returns BAR: FOO should return BAR: BAR

If i've got it right, the second call of new BAR() (after new FOO()) should return BAR: BAR not BAR: FOO as at the moment.

One possible solution for this problem is an complete rewrite of the clone function in something like this:

function clone(obj) {
  return eval("("+obj.toString()+")"); // the same as eval(uneval(obj));
}

But this approach has an BIG downside, you can't pass any dynamically created objects.

Any ideas?

Upvotes: 1

Views: 2035

Answers (5)

Stanislav Müller
Stanislav Müller

Reputation: 439

I just wanted to show anyone my solution for the problem above.

function cloneClass (func) {

  if(typeof func == 'function') {

    var prototype = {};
    for (var prop in func.prototype) {
      prototype[prop] = func.prototype[prop];
    };

    var Class = function() {
      var _this = this;
      var constructor = function() {
        func.apply(_this, arguments);
      };
      constructor.prototype = _this;

      for (var property in func) {
        if(property != "prototype") {
          constructor[property] = func[property];
        }
      };

      return constructor;
    };

    Class.prototype = prototype;
    return new Class();
  }

  return func;

};

Try to dig into it to understand how this is working. Does anyone can see any problems with this implementation, memory leaks etc.?

Upvotes: 0

Christoph
Christoph

Reputation: 169613

function clone(obj) {
    if(typeof obj !== 'undefined') {
        clone.prototype = Object(obj);
        return new clone;
    }
}

function Foo() {} // base class

function Bar() {} // derived class
Bar.prototype = clone(Foo.prototype); // inherit from `Foo.prototype`

Upvotes: 0

Rojo
Rojo

Reputation: 146

There is an another change that needs to be made, in addition to that mentioned by SolutionYogi. In FOO, you're passing BAR to be cloned, but BAR is constructor (typeof BAR == "function"), so it's going to fail the first test the clone function, and your return value will be a reference to an unchanged BAR. And that means myBAR.prototype is not a clone of BAR.prototype, but a reference to it.

In order to actually create a new constructor, not just a ref, I think you'll have to use eval--adding something like:

if (typeof obj == "function) {
    eval("var temp = " + obj + ";");
    return temp;
}

There are other considerations (as Alex points out), but adding the above, should cause your test case to be successful.

Upvotes: 0

SolutionYogi
SolutionYogi

Reputation: 32243

The problem is how you are cloning the 'prototype'

The following line

myBAR.prototype = jQuery.extend(true, myBAR.prototype, this); // deep cloning 

You are not only cloning the 'prototype', you are also cloning the 'name' property.

If you replace above line with

myBAR.prototype = jQuery.extend(true, myBAR.prototype, this.prototype); // deep cloning 

Your code will now return

new FOO(); // returns FOO:FOO and BAR:BAR
new BAR(); // returns BAR:BAR

Upvotes: 1

Alex
Alex

Reputation: 36586

The issue with cloning a javascript object is how deep do you decide to go?

Consider i have the following object:

var obj = { 
    prop : {
        n1prop : {
            hello : 'world';
        }
    }
};

This means i would have to traverse all properties that are of type 'object' which could get quite expensive if you have a deep nesting.

If you have a simple 1 level object you could just use a simple reflection for loop and create a new object literal. NOTE: This won't copy methods of the original object.

function clone(obj) {
   var cloned;

   if (obj && typeof obj === 'object') {
       cloned = {};
       for (var p in obj) {
           if (obj.hasOwnProperty(p) && typeof obj[p] !== 'function') {
               cloned[p] = obj[p]
           }
       }
       return cloned;
   }
   else {
       return null;
   }
}

Upvotes: 0

Related Questions