deepcopy
Version:
deep copy data
113 lines (95 loc) • 2.7 kB
JavaScript
import { detectType } from './detector.mjs';
import { get, isCollection, set } from './collection.mjs';
import { copy } from './copier.mjs';
/**
* deepcopy function
*
* @param {*} value
* @param {Object|Function} [options]
* @return {*}
*/
export default function deepcopy(value, options = {}) {
if (typeof options === 'function') {
options = {
customizer: options
};
}
const {
// TODO: before/after customizer
customizer
// TODO: max depth
// depth = Infinity,
} = options;
const valueType = detectType(value);
if (!isCollection(valueType)) {
return recursiveCopy(value, null, null, null, customizer);
}
const copiedValue = copy(value, valueType, customizer);
const references = new WeakMap([[value, copiedValue]]);
const visited = new WeakSet([value]);
return recursiveCopy(value, copiedValue, references, visited, customizer);
}
/**
* recursively copy
*
* @param {*} value target value
* @param {*} clone clone of value
* @param {WeakMap} references visited references of clone
* @param {WeakSet} visited visited references of value
* @param {Function} customizer user customize function
* @return {*}
*/
function recursiveCopy(value, clone, references, visited, customizer) {
const type = detectType(value);
const copiedValue = copy(value, type);
// return if not a collection value
if (!isCollection(type)) {
return copiedValue;
}
let keys;
switch (type) {
case 'Arguments':
case 'Array':
keys = Object.keys(value);
break;
case 'Object':
keys = Object.keys(value);
keys.push(...Object.getOwnPropertySymbols(value));
break;
case 'Map':
case 'Set':
keys = value.keys();
break;
default:
}
// walk within collection with iterator
for (let collectionKey of keys) {
const collectionValue = get(value, collectionKey, type);
if (visited.has(collectionValue)) {
// for [Circular]
set(clone, collectionKey, references.get(collectionValue), type);
} else {
const collectionValueType = detectType(collectionValue);
const copiedCollectionValue = copy(collectionValue, collectionValueType);
// save reference if value is collection
if (isCollection(collectionValueType)) {
references.set(collectionValue, copiedCollectionValue);
visited.add(collectionValue);
}
set(
clone,
collectionKey,
recursiveCopy(
collectionValue,
copiedCollectionValue,
references,
visited,
customizer
),
type
);
}
}
// TODO: isSealed/isFrozen/isExtensible
return clone;
}