Skip to content

Instantly share code, notes, and snippets.

@imbcmdth
Created September 12, 2012 19:47
Show Gist options
  • Save imbcmdth/3709419 to your computer and use it in GitHub Desktop.
Save imbcmdth/3709419 to your computer and use it in GitHub Desktop.
Robust deep copying of objects w/ support for cycles
var deepCopy = (function () {
var funcBlacklist = ['caller', 'arguments', 'prototype' ],
primitiveCloner = makeCloner(clonePrimitive),
cloneFunctions = {
'[object Null]': primitiveCloner,
'[object Undefined]': primitiveCloner,
'[object Number]': primitiveCloner,
'[object String]': primitiveCloner,
'[object Boolean]': primitiveCloner,
'[object RegExp]': makeCloner(cloneRegExp),
'[object Date]': makeCloner(cloneDate),
'[object Function]': makeRecCloner(makeCloner(cloneFunction), funcBlacklist),
'[object Object]': makeRecCloner(makeCloner(cloneObject)),
'[object Array]': makeRecCloner(makeCloner(cloneArray))
};
function clonePrimitive(primitive) {
return primitive;
}
function cloneRegExp(regexp) {
return RegExp(regexp);
}
function cloneDate(date) {
return new Date(date.getTime());
}
function cloneFunction(fn) {
var copy = Function('return ' + fn.toString() + ';')();
copy.prototype = Object.getPrototypeOf(fn);
return copy;
}
// This will not properly clone `constructed` objects
// because it is impossible to know what arguments the constructor
// was originally invoked with.
function cloneObject(object) {
return Object.create(Object.getPrototypeOf(object));
}
function cloneArray(array) {
return [];
}
function makeCloner(cloneThing) {
return function (thing, thingStack, copyStack) {
var copy = cloneThing(thing);
thingStack.push(thing);
copyStack.push(copy);
return copy;
};
}
function makeRecCloner(cloneThing, blacklist) {
return function (thing, thingStack, copyStack) {
var clone = this;
return Object.getOwnPropertyNames(thing).
filter(function (prop) {
return !blacklist || blacklist.indexOf(prop) === -1;
}).
reduce(function (copy, prop) {
var thingOffset = thingStack.indexOf(thing[prop]);
copy[prop] = (thingOffset === -1) ?
clone(thing[prop]) :
copyStack[thingOffset];
return copy;
}, cloneThing(thing, thingStack, copyStack));
};
}
return function (cloneMe) {
var thingStack = [],
copyStack = [];
function clone(thing){
var type = Object.prototype.toString.call(thing);
return cloneFunctions[type].call(clone, thing, thingStack, copyStack);
}
return clone(cloneMe);
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment