Skip to content

Instantly share code, notes, and snippets.

@julenka
Created June 5, 2014 16:13
Show Gist options
  • Save julenka/a1256d6d4b670005ce8e to your computer and use it in GitHub Desktop.
Save julenka/a1256d6d4b670005ce8e to your computer and use it in GitHub Desktop.
Performs deep clone on javascript object. Courtesy of @nneonneo
function deepClone(o) {
var origObjs = {};
var objCache = {};
var nextObjId = 0;
var propName = "__clone_prop_" + Math.random();
recurseClone = function(o) {
// base case
switch(typeof(o)) {
case 'undefined':
case 'boolean':
case 'number':
case 'string':
case 'function':
return o;
case 'object':
break;
default:
console.log("warning: unknown typeof " + typeof(o));
return o;
}
if(o === null)
return o;
// if we have already cloned this object previously (e.g. circular reference)
// then just return the cached version
if(propName in o)
return objCache[o[propName]];
// co stands for 'cloned object'
var co;
// get the type of the object
var baseType = Object.prototype.toString.apply(o); // [object X]
baseType = baseType.substr(8, baseType.length - 9);
// more base cases
switch(baseType) {
case 'Boolean':
co = new Boolean(o.valueOf());
break;
case 'Number':
co = new Number(o.valueOf());
break;
case 'String':
co = new String(o.valueOf());
break;
case 'Date':
co = new Date(o.valueOf());
break;
case 'RegExp':
co = new RegExp(o);
break;
/* File, Blob, FileList, ImageData */
case 'Array':
// for an array, just get an array of the given length
co = new Array(o.length);
break;
default:
console.log("warning: unknown object type " + baseType);
/* fall through */
case 'Object':
// for a generic object, create its prototype
co = Object.create(Object.getPrototypeOf(o));
break;
}
// get all the properties of this object, prepare for copying
var props = Object.getOwnPropertyNames(o);
// save this object (well,the cloned version) in the cache
o[propName] = nextObjId++;
objCache[o[propName]] = co;
origObjs[o[propName]] = o;
// copy all values of this property
for(var i=0; i<props.length; i++) {
var prop = Object.getOwnPropertyDescriptor(o, props[i]);
// if this property has a value, update it
if('value' in prop) {
prop.value = recurseClone(prop.value);
}
// define a property the 'right' way, assigning its descriptor
// this properly preserves the visibility of the property which is nice.
Object.defineProperty(co, props[i], prop);
}
return co;
}
var newObj = recurseClone(o);
/* cleanup */
// getOwnPropertyNames returns an array of properties that are found
// directly upon a given object. corresponds both the enumerable AND non-enumerable
// properties of the object.
//
var props = Object.getOwnPropertyNames(origObjs);
for(var i=0; i<props.length; i++) {
// delete the 'nextObjId' identifier of a property in this object
delete origObjs[props[i]][propName];
}
return newObj;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment