Memoization with Lazy Getters
snippetTIL JavaScript getters can simply be deleted, even if there’s no corresponding setter:
let obj = {
data: 123,
get hash() {
return this.data * Math.random();
}
};
obj.hash = 666; // 💥 TypeError: setting getter-only property "hash"
delete obj.hash;
obj.hash = 888; // ✅
We can use this to avoid redundant computations, replacing our getter with the respective value upon first invocation:
let obj = {
data: 123,
get hash() {
let value = this.data * Math.random();
delete this.hash;
this.hash = value;
return value;
}
};
We could alternatively employ
Object.defineProperty
to the same effect:
let obj = {
data: 123,
get hash() {
let value = this.data * Math.random();
Object.defineProperty(this, "hash", {
value,
writable: false
});
return value;
}
};
This might be preferable because it doesn’t (temporarily) change our object’s
shape; delete
can trip up
engines’ performance optimizations –
though I haven’t researched this particular scenario.
Note that for some scenarios, we might need to distinguish between instance and prototype properties. That also opens up an intriguing opportunity:
class Record {
set data() {
this._data = data;
delete this.hash; // resets memoization
}
get hash() {
let value = this._data * Math.random();
Object.defineProperty(this, "hash", {
value,
configurable: true, // enables reset
});
return value;
}
}
Here we’re caching our value in an instance property, which takes precedence
over the prototype’s getter. We can later delete
that instance property so
that hash
is recalculated via the prototype when accessed again.