weaken-it
v1.1.5
Published
WeakMap utility functions and related patterns
Downloads
20
Maintainers
Readme
weaken-it
Lightweight tree-shakeable WeakMap utility functions and related patterns, with typescript support.
Every weakness contains within itself a strength Shūsaku Endō
Photo by Herbert Goetsch on Unsplash
:man_technologist: Installation
Npm
npm i weaken-it
CDN
<script type="module">
const { wit } = await import("https://cdn.jsdelivr.net/npm/weaken-it@latest")
// store to context
wit(SomeReference, ContextNamespace, AnyValue)
// retrieve
wit(SomeReference, ContextNamespace)
</script>
:wrench: How it works
At its core it's just a temporary namespaced key-value store built on top of native js WeakMap and Map, using a set of personal conventions enforced by weakenIt()
function. Both WeakMap and Map were chosen over plain objects for their advantages in terms of r/w speed and key flexibility.
- Creates a WeakMap,
wStore
- WeakMap ensures that each instance is stored once and its associated context expires with it
weakenIt
ensures that each Map context is stored / recreated only once and always reused- Then
weakenIt
, or even shorterwit
, stores / gets any key-value from the related context - When reference is lost or
wStore.prototype.deleted || wDel
, its context gets garbage-collected
:thinking: Why
I was very excited about WeakRef and FinalizationRegistry coming to javascript but both their behaviours are not yet guaranteed, making them very hard to reason about, at least for me.
:ring_buoy: WeakMap and WeakSet to the rescue!
- They ensure stored data will expire after the bound instance, or will always be available as long the associated reference exists
- They enable patterns I reach for when I need to extend libraries, js natives, work with dom, store temporary data or when I'm generally concerned about potential memory leaks
- It can safely bridge context data inside closures or where unreachable
- They're fast, native, memory-efficient by nature, fully supported and reliable
- They give me peace of mind :relieved:
:muscle: Usage
For more examples, please refer to tests folder.
import { weakenIt } from 'weaken-it'
// augment an external library or native
class Example extends SomeLibraryOrNative {
constructor(extraData, ...libraryRequired) {
super(...libraryRequired)
// extraData never outlives the instance
weakenIt(this, 'temp', extraData + 'Example constructor')
}
overridden(...methodArgs) {
// bridged inside
const extraData = weakenIt(this, 'temp')
doSomethingWith(temp)
// still works without modifying the original implementation
return SomeLibraryOrNative.prototype.overridden.apply(this, methodArgs)
}
}
let exampleInstance = new Example(
'extra data from: ',
'library needed data'
)
function useExtraData() {
console.log(weakenIt(example, 'temp'))
}
useExtraData()
// extra data from: Example constructor
example = null
useExtraData()
// undefined (gc'ed)
:toolbox: Available exports
Core
- weakenIt, core function to interact with contexts
- wStore, stores all contexts
Shortcuts
- wit, alias to weakenIt
- wDel, hard reset context
- wCtx, gets the entire context
- wCast, casts a copy of the context as object
- wSure, upsert if stored is nullish
- wMap, new Map(), dirt-cheap iterable WeakMap
- wSet, new Set(), dirt-cheap iterable WeakSet
- wKV, creates a Object.create(null)
- wArr, creates []
- wObj, creates {}
Utils
- wMemo, memoized fn
- wCount, global counter
:ballot_box_with_check: TODO
[x] Import and standardize other patterns [x] Randomize tests [x] Documentation
Disclaimer
This library is very opinionated, mainly bult for reusability between personal projects. It encapsulates and summarizes some of my most recurring personal approaches involving native WeakMap, which I really :heart: