Cloning javascript objects
Facts - HTML and Javascript
Thursday, 05 March 2009 10:07

Objects in javascript are assigned by reference. Sometimes we want to make a deep copy of an object rather than copying a reference. There are several solutions for this problem, and unfortunately some solutions have disadvantages.

Constructs with eval/uneval don't work with every browser. Prototyping the global Object can mess up things especially when including other javascript libraries.

A good solution is using a for ..in loop:

function clone(o) {
  function c(o) {
    for (var i in o) {
      this[i] = o[i];
    }
  }
 
  return new c(o);
};

Another browser independent solution extends the JSON library json2.js:

JSON.clone = function (obj) {
  return JSON.parse( JSON.stringify( obj ) );
};
 
var obj = { "date": "2009-03-05", "amount": "0.1" };
var clonedObj = JSON.clone(obj);

A more elegant browser independent solution comes from Marijn Haverbeke (Elegant JavaScript):

function clone(o) {
  function OneShotConstructor(){}
  OneShotConstructor.prototype = o;
  return new OneShotConstructor();
}

The JSON.clone method only copies data members: it does not copy any function members of the object. The other two clone functions copy both the function and the data members.

Strictly spoken the first method does not produce a real clone either since it adds a member function c to the cloned object.

The first method has an advantage above the second and the third method. With the first method objects can not only be cloned but also be filtered by adding an extra function argument to the clone function.

var obj = { "date": "2009-03-05", "amount": "0.1" };
 
function filter0_1(val)
{ if (val=="0.1") return false; return true; }
 
function clone(o, filter) {
  function defaultFilter(val) { return true; }
  function c(o, filter) {
    for (var i in o) {
      if (filter(o[i])) this[i] = o[i];
    }
  }
  if (arguments.length==1) filter = defaultFilter;
  return new c(o, filter);
};
 
alert(JSON.stringify(clone(obj, filter0_1))); // amount field has not been copied
alert(JSON.stringify(clone(obj)));            // full original object has been copied

Note that the filtered clone adds both a function c and an extra function defaultFilter to the cloned object.