memo-obj
Version:
A deep structural memoization utility for objects to reuse identical shapes and references efficiently.
86 lines (85 loc) • 3.53 kB
JavaScript
import WeakRefsSet from "./weak-refs-set.mjs";
const fastLookupMap = new WeakMap();
const circularLookupMap = new WeakMap();
const memoRefsSet = new WeakRefsSet();
const backupRefs = new WeakRefsSet();
export function memoObj(object, options) {
const { forceOptimizationAsFrozen = false, } = options || {};
if (object && typeof object === "object") {
let ref = circularLookupMap.get(object);
let memoized = ref?.deref();
if (memoized !== undefined) {
return memoized;
}
ref = fastLookupMap.get(object);
memoized = ref?.deref();
if (memoized !== undefined && (forceOptimizationAsFrozen || Object.isFrozen(object))) {
return memoized;
}
const objectStringKeys = new Set(Object.getOwnPropertyNames(object));
const objectSymbolKeys = new Set(Object.getOwnPropertySymbols(object));
const proto = Object.getPrototypeOf(object);
const compareValues = (key) => {
if (memoized === undefined)
return false;
const objectChild = object[key];
const memoizedChild = memoized[key];
return Object.is(objectChild, memoizedChild) || Object.is(memoObj(objectChild), memoObj(memoizedChild));
};
const compareWithMemoized = () => {
if (memoized === undefined)
return false;
if (!Object.is(proto, Object.getPrototypeOf(memoized)))
return false;
const memoizedStringKeys = Object.getOwnPropertyNames(memoized);
const memoizedSymbolKeys = Object.getOwnPropertySymbols(memoized);
if (objectStringKeys.size !== memoizedStringKeys.length || objectSymbolKeys.size !== memoizedSymbolKeys.length)
return false;
if (!(memoizedStringKeys.every(key => objectStringKeys.has(key)) && memoizedSymbolKeys.every(key => objectSymbolKeys.has(key))))
return false;
circularLookupMap.set(object, ref);
const result = memoizedStringKeys.every(compareValues) && memoizedSymbolKeys.every(compareValues);
circularLookupMap.delete(object);
return result;
};
if (memoized !== undefined) {
if (compareWithMemoized()) {
return memoized;
}
else {
fastLookupMap.delete(object);
}
}
for (const ref_ of memoRefsSet) {
ref = ref_;
memoized = ref.deref();
if (compareWithMemoized()) {
backupRefs.add(object);
if (forceOptimizationAsFrozen || Object.isFrozen(object)) {
fastLookupMap.set(object, ref);
}
return memoized;
}
}
for (const ref_ of backupRefs) {
ref = ref_;
memoized = ref.deref();
if (compareWithMemoized()) {
memoRefsSet.addRef(ref);
backupRefs.deleteRef(ref);
backupRefs.add(object);
if (forceOptimizationAsFrozen || Object.isFrozen(object)) {
fastLookupMap.set(object, ref);
}
return memoized;
}
}
ref = new WeakRef(object);
memoRefsSet.addRef(ref);
if (forceOptimizationAsFrozen || Object.isFrozen(object)) {
circularLookupMap.set(object, ref);
}
}
return object;
}
export default memoObj;