Skip to content

Instantly share code, notes, and snippets.

@dsherret
Last active November 23, 2019 05:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dsherret/d092018544100b7f565170f0ec86330f to your computer and use it in GitHub Desktop.
Save dsherret/d092018544100b7f565170f0ec86330f to your computer and use it in GitHub Desktop.
Cloning an immutable object via a proxy.
// I wrote this to test if it would be faster for my scenario and it wasn't... it was much much slower.
// So I'm throwing it away here on a gist. Perhaps someone would still find it useful.
/**
* Mimicks the behaviour of cloning via a proxy for when the underlying object
* will never change, but the clone might.
* @param originalObject - Object to create a proxied clone of.
*/
export function proxiedClone<T extends object>(originalObject: T): T {
const proxies = new Map<object, object>();
return getOrCreateObjectProxy(originalObject) as T;
function getOrCreateObjectProxy<T extends object>(obj: T): T {
let newObj = proxies.get(obj) as T | undefined;
if (newObj == null) {
newObj = createCloneProxy(obj);
proxies.set(obj, newObj);
}
return newObj;
}
function createCloneProxy<T extends object>(obj: T): T {
const deletedProps: any = {};
const props: any = {};
return new Proxy(obj, {
get(target, prop) {
if (prop in deletedProps)
return undefined;
if (prop in props)
return props[prop];
const value = (target as any)[prop];
if (typeof value === "object")
return props[prop] = cloneItem(value);
return value;
},
set(target, prop, value) {
props[prop] = value;
delete deletedProps[prop];
return true;
},
deleteProperty(target, prop) {
deletedProps[prop] = undefined;
delete props[prop];
return true;
},
has(target, prop) {
if (prop in deletedProps)
return false;
return prop in props || prop in target;
},
ownKeys(target) {
return [
...Object.keys(target).filter(prop => !(prop in deletedProps)),
...Object.keys(props).filter(prop => !(prop in target))
];
},
enumerate(target) {
return this.ownKeys!(target as any); // why is this assertion necessary?
},
setPrototypeOf(target, v) {
throw new Error("Not implemented method for clone proxy.");
},
defineProperty(target, p, attributes) {
throw new Error("Not implemented method for clone proxy.");
}
});
}
function cloneItem(item: object) {
if (item instanceof Array)
return cloneArray(item);
return getOrCreateObjectProxy(item);
}
function cloneArray(array: unknown[]): unknown[] {
return array.map(item => typeof item === "object" ? item === null ? item : cloneItem(item) : item);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment