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