123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 |
- 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;
- }
|