pop-clone
Version:
A polymorphic clone operator for arbitrary object graphs
64 lines (60 loc) • 2.22 kB
JavaScript
var MiniMap = require("mini-map");
var getPrototypeOf = Object.getPrototypeOf;
var objectPrototype = Object.prototype;
/**
* Creates a deep copy of any value. Values, being immutable, are returned
* without alternation. Forwards to <code>clone</code> on objects and arrays.
*
* @function clone
* @param {Any} value a value to clone
* @param {Number} depth an optional traversal depth, defaults to infinity. A
* value of <code>0</code> means to make no clone and return the value
* directly.
* @param {Map} memo an optional memo of already visited objects to preserve
* reference cycles. The cloned object will have the exact same shape as the
* original, but no identical objects. Te map may be later used to associate
* all objects in the original object graph with their corresponding member of
* the cloned graph.
* @returns a copy of the value
*/
module.exports = cloneOperator;
function cloneOperator(value, depth, memo) {
if (value && value.valueOf) {
value = value.valueOf();
}
if (depth == null) { // null or undefined
depth = Infinity;
} else if (depth === 0) {
return value;
}
if (value && typeof value === "object") {
memo = memo || new MiniMap();
if (!memo.has(value)) {
if (value && typeof value.clone === "function") {
memo.set(value, value.clone(depth, memo));
} else {
var isArray = Array.isArray(value);
var prototype = getPrototypeOf(value);
if (
isArray ||
prototype === null ||
prototype === objectPrototype
) {
var clone = isArray ? [] : {};
memo.set(value, clone);
for (var key in value) {
clone[key] = cloneOperator(
value[key],
depth - 1,
memo
);
}
} else {
throw new Error("Can't clone " + value);
}
}
}
return memo.get(value);
}
return value;
}