Recently I've been facing a case where I needed to create some unique references.
I was creating a JS API and needed to maintain some internal state relative to elements created by the API.
In practice, I had a WeakMap
where I maintained a private state for each public reference returned by the API.
Why a
WeakMap
?The keys of a
WeakMap
are weak references, which means as soon as no one else holds the reference of a key, the key and its value get removed from the map and garbage collected.
The type of the public references didn't matter to me, I just needed unique references.
But it would be better if those references were easy to debug, which meant having at least a relevant toString()
method.
⛔ Non choices ⛔
{}
What's the easiest way to create a unique reference in JS? Initializing an empty object!
But that doesn't satistfy the "easy to debug" requirement:
const ref = {}
console.log(`debug: ${ref}`)
// Output:
// debug: [object Object]
String
A string is easy to debug, you just print it:
let nextId = 1
const ref = `ref #${nextId++}`
console.log(`debug: ${ref}`)
// Output:
// debug: ref #1
But a string isn't a unique reference, anyone may obtain the same reference very easily.
And as a matter of fact, a WeakMap
doesn't accept strings as keys, neither any other primitive type!
Symbol
The purpose of Symbol
is to create unique references!
There I thought I had my solution...
let nextId = 1
const ref = Symbol(`ref #${nextId++}`)
console.log(`debug: ${ref}`)
// Output:
// TypeError: Cannot convert a Symbol value to a string
Wow! Far from what I expected...
Even though Symbol
has a toString()
method, putting it in a string interpolation yells at me!
Furthermore symbols are primitive values, hence WeakMap
won't accept these as keys!
🟢 Solutions 🟢
class
There I found myself forced to write my own class
:
let nextId = 1
class Ref {
constructor() { this.id = nextId++ }
toString() { return `ref #${this.id}` }
}
const ref = new Ref()
console.log(`debug: ${ref}`)
// Output:
// debug: ref #1
This is exactly what I wanted, but it feels like I'm squashing a fly with a sledgehammer...
Do I really have to create my own type?
new String()
Yes it's possible to use the String constructor, and it is equivalent to using a class
:
let nextId = 1
const ref = new String(`ref #${nextId++}`)
console.log(`debug: ${ref}`)
// Output:
// debug: ref #1
It does create a unique reference, which isn't of a primitive type.
Hence it may be used as a key for a WeakMap
!
Your turn!
Do you have any other ideas? Share them with me!
Thanks for reading, give a ❤️, leave a comment 💬, and follow me to get notified of my next posts.