Last active
July 4, 2021 11:18
-
-
Save jhunterkohler/ee7cfd3a49c1343ab540959d61887733 to your computer and use it in GitHub Desktop.
Simple memoizing function and weak value map
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const memoize = (() => { | |
const wvm = new WeakValueMap(); | |
return function (func, hash = JSON.stringify) { | |
return function () { | |
const key = hash.apply(this, arguments); | |
if (wvm.has(key)) { | |
return wvm.get(key); | |
} | |
const ret = func.apply(this, arguments); | |
map.set(key, ret); | |
return ret; | |
}; | |
}; | |
})(); | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function isObject(value) { | |
return ( | |
(typeof value == "object" && value !== null) || | |
typeof value == "function" | |
); | |
} | |
function isNullish(value) { | |
return typeof value == "undefined" || value === null; | |
} | |
function defineInstanceProperty(cls, key, value) { | |
return Reflect.defineProperty(cls.prototype, key, { | |
value, | |
configurable: true, | |
enumerable: false, | |
writable: typeof value == "function", | |
}); | |
} | |
class WeakValueMapIterator { | |
static #_ = (() => { | |
defineInstanceProperty( | |
this, | |
Symbol.toStringTag, | |
"WeakValueMap Iterator" | |
); | |
})(); | |
#base; | |
#iter; | |
#type; // keys, values, or entries | |
#done = false; | |
/** | |
* @param {Map} base | |
* @param {"keys"|"values"|"entries"} [type="entries"] | |
*/ | |
constructor(base, type = "entries") { | |
this.#type = type; | |
this.#base = base; | |
this.#iter = base[Symbol.iterator](); | |
} | |
next() { | |
let _next; | |
do { | |
if (_next) { | |
this.#base.delete(iter.value[0]); | |
} | |
_next = this.#iter.next(); | |
} while ( | |
_next.value?.[1] instanceof WeakRef && | |
_next.value?.[1].deref === undefined | |
); | |
this.#done = _next.done; | |
if (this.#type == "keys") { | |
_next.value = _next.value?.[0]; | |
} else if (this.#type == "values") { | |
_next.value = _next.value?.[1]; | |
} | |
return _next; | |
} | |
get done() { | |
return this.#done; | |
} | |
[Symbol.iterator]() { | |
return this; | |
} | |
} | |
class WeakValueMap { | |
static Iterator = WeakValueMapIterator; | |
static #_ = (() => { | |
defineInstanceProperty(this, Symbol.toStringTag, "WeakValueMap"); | |
defineInstanceProperty(this, Symbol.iterator, this.prototype.entries); | |
})(); | |
#base = new Map(); | |
constructor(entries) { | |
if (!isNullish(entries)) { | |
if (!isObject(entries)) { | |
throw new TypeError( | |
typeof entries + | |
" is not iterable (cannot read property " + | |
String(Symbol.iterator) + | |
")" | |
); | |
} | |
for (const entry of entries) { | |
if (!isObject(entry)) { | |
throw new TypeError( | |
`Iterator value ${entry} is not an entry object` | |
); | |
} | |
this.set(entry[0], entry[1]); | |
} | |
} | |
} | |
get(key) { | |
let value = this.#base.get(key); | |
if (value instanceof WeakRef) { | |
value = value.deref(); | |
if (value === undefined) { | |
this.#base.delete(key); | |
} | |
} | |
return value; | |
} | |
set(key, value) { | |
this.#base.set(key, isObject(value) ? new WeakRef(value) : value); | |
return this; | |
} | |
has(key) { | |
if (!this.#base.has(key)) { | |
return false; | |
} | |
let value = this.#base.get(key); | |
if (value instanceof WeakRef) { | |
value = value.deref(); | |
if (value === undefined) { | |
this.#base.delete(key); | |
return false; | |
} | |
} | |
return true; | |
} | |
delete(key) { | |
return this.has(key) && this.#base.delete(key); | |
} | |
clear() { | |
this.#base.clear(); | |
} | |
entries() { | |
return new WeakValueMapIterator(this.#base); | |
} | |
forEach(callbackfn, thisArg) { | |
for (const [key, value] of this) { | |
callbackfn.call(thisArg, value, key, this); | |
} | |
} | |
keys() { | |
return new WeakValueMapIterator(this.#base, "keys"); | |
} | |
values() { | |
return new WeakValueMapIterator(this.#base, "values"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment