Created
June 5, 2014 16:13
-
-
Save julenka/a1256d6d4b670005ce8e to your computer and use it in GitHub Desktop.
Performs deep clone on javascript object. Courtesy of @nneonneo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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