Deep Clone an Object

I came across an example of a shallow copy script last night, and decided I would set myself the task of coming up with a deep copy version.

For one thing I wanted to use recursion, a technique that I’m still trying to get on top of.

My first stage. It was pretty evident that it needed streamlining.

var deepClone = function(o, clone) {
  var i = 0, prop, len,
  clone = clone || (o instanceof Array ? [] : {});
  
  if (o instanceof Array && (len = o.length)){
    for (; i < len; i+=1){
	  clone.push ((o[i] instanceof Object && o[i].constructor != Function)
	    ? deepClone(o[i], (o[i] instanceof Array ? [] : {}))
	    : o[i]
	  )
	}
  } else if (o instanceof Object) {
    for (prop in o){
	  if (o.hasOwnProperty(prop)){
	    clone[prop] = (o[prop] instanceof Object && o[prop].constructor != Function)
	      ? deepClone(o[prop], (o[prop] instanceof Array ? [] : {}))
	      : o[prop];
	  }
	}
  }
  return clone;
};

I’ve so far managed to get it down to this

var deepClone = function(o) {
  if (o instanceof Object && o.constructor != Function){
    var clone = o instanceof Array ? [] : {}, prop;

	for (prop in o){
	  if (o.hasOwnProperty(prop)){ 
	    clone[prop] = (o[prop] instanceof Object) 
	      ? deepClone(o[prop]) 
	      : o[prop]; 
	  }
	}
    return clone;
  }
  return o;
};

and this is a working example

// ------ An object with various properties, subproperties etc -------
var obj = {
	propA : 5,
	propB : [ 1, 3, 5, { 
	    val1 : 3, 
		val2 : 5, 
		val3 : 7, 
		valArray : [ 9, 11, 13 ] 
	  }
	],
	propC : { 
	  fruit1 : 'apples', 
	  fruit2 : 'bananas', 
	  fruit3 : 'apricots', 
	  fruit4 : 'grapes',
	  fruitObj : {
	    miscFunc : function(){ alert (this.fruit1); return this.fruit4;},
		vegArray : [ 'cabbage', 'carrots', 'mushrooms', { 
		    seasoning1: 'salt',
			seasoning2: 'pepper'
		  } 
		]
	  }
	},
	propD : 'Just a string',
	propE : new RegExp('^[a-zB-Z]+$','gi')
};
// -------------------------------------------------------------------------

var deepClone = function(o) {
  // If o is a function or a primitive return it
  // Check if o is an object or an array object and set clone accordingly
  if (o instanceof Object && o.constructor != Function){
    var clone = o instanceof Array ? [] : {}, prop;
  
  // Check to see if property is an own property
  // hasOwnProperty responds true on Array properties
  // If the property is an Object then call deepClone( to process it and return )
  // otherwise assign the primitive value
  // finally return the clone
	for (prop in o){
	  if (o.hasOwnProperty(prop)){ 
	    clone[prop] = (o[prop] instanceof Object) 
	      ? deepClone(o[prop]) 
	      : o[prop]; 
	  }
	}
    return clone;
  }
  return o;
};

// --------- A quick test to see if we have copies or references ---
var cloneObj = deepClone(obj);
obj.propC.fruitObj.vegArray[2] = 'cauliflower';
console.log(cloneObj.propC.fruitObj.vegArray[2]); // mushrooms

cloneObj.propB[2] = 22;
console.log(obj.propB[2]); //5

console.log(obj);
console.log(cloneObj);

I’ve just thrown in a RegExp object and noticed it fails, so I have to address that.

I would be interested in any feedback. Are there any glaring mistakes with my method or improvements that could be made?

Another technique I’m interested in is memoization. I know it can be used effectively for things like factorials, but would it be feesible for something like a deep copy?

Any thoughts from those in the know would be greatly appreciated;)

RLM

Right I’m here with this little exercise.

var deepClone = function(o) {
  if (o.constructor === Array || {}.toString.apply(o) === '[object Object]'){
    var clone = (o.constructor === Array) ? [] : {}, prop;
  
	for (prop in o){
	  if (o.hasOwnProperty(prop)){ 
	    clone[prop] = (o[prop] instanceof Object) 
	      ? deepClone(o[prop]) 
	      : o[prop]; 
	  }
	}
    return clone;
  }
  return o;
};

I’ve been working through the type checks. At one point I went for ‘o.constructor === Object’, however for obvious reasons this fails if you want to clone a prototype object. I then remembered [object Object].


if (o.constructor === Array || {}.toString(o) === '[object Object]'){
  var clone = (o.constructor === Array) ? [] : {}, prop;

Could it be improved? Is it flawed? Is there a string method that returns [array Array]?

I’m sure there are other questions.

Edit: Again it’s flawed and fails on other types of object like a RegExp.

Edit2: Simple fix.
Instead of {}.toString(o) === ‘[object Object]’ it should be {}.toString.apply(o) === ‘[object Object]’

RLM

Just coming back to this.

I think the answer is yes it’s flawed. A guy on another forum had a go at a similar exercise and made less of a meal of it than I did.

The main problem with my code comes down to type checking and the use of ‘instanceof Object’

The problem is that ‘instanceof Object’ returns true for functions, which means my code is passing functions into the recursice function call.

typeof … == ‘object’, seems to be a better alternative.

var fn = function(){var x = 5;}
var xOb = {};

console.log (typeof fn == 'object'); // false
console.log (typeof xOb == 'object'); // true
console.log (fn instanceof Object); // true

Question: Functions are first class objects. You can add properties to functions just like other objects. I haven’t tested this, but I’m now wondering whether they should in fact be passed through. Over complicating things maybe.

Anyway

Update 1:

var deepClone = function(o) {
    var clone = (o.constructor === Array) ? [] : {}, prop;
 
    for (prop in o){
      if (o.hasOwnProperty(prop)){
        clone[prop] = (typeof o[prop] == 'object')
          ? deepClone(o[prop])
          : o[prop];
      }
    }
    return clone;
};

I wanted to try and utilise this in my literal object extend code, and due to the fact I need to pass ‘this’ from my constructor the extend needs to take 2 parameters. Parent and Child. I believe what I have ended up with here is a pretty generic version.

Update 2:

var deepCopy = function(parent, child){
	for(var prop in parent) {
	  if (parent.hasOwnProperty(prop))
        child[prop] = (typeof parent[prop] === 'object') 
	    ? deepCopy(parent[prop], (parent[prop].constructor === Array)? [] : {}) 
	    : parent[prop];      
    }
    return child;
};

RLM