typescript - Vad är det bästa sättet att cacha en getter (utan dekoratörer)?
Praktiken att "cacha" en getter är ett sätt att bara anropa ett värde en gång och återanvända samma värde utan att beräkna om det. Jag letar efter det "bästa" sättet att cacha en getter, med bästa menar jag med kortast syntax, med typning. Det finns många bibliotek där ute som helt enkelt låter dig använda en dekorator som @cache över gettern och den fungerar som jag har beskrivit, jag vill inte använda dekoratorer.
Typescript-lekplats
Här är ett sätt:
type NoUndefined<T> = T extends undefined ? never : T;
function isNoUndefined <T> (value: T): value is NoUndefined<T> {
return typeof value !== 'undefined'
}
function handleGetter <T>(value:T, operation: () => NoUndefined<T>): NoUndefined<T> {
if (isNoUndefined(value)) return value;
return operation()
}
class CalendarDate extends Date {
#day: number | undefined = undefined
get day () {
return this.#day = handleGetter(this.#day, () => {
console.log('runs once')
return this.getDate()
})
}
}
const c = new CalendarDate()
c.day
c.day
c.day
c.day
Svar #1
Ett möjligt tillvägagångssätt är att skriva en funktion som ersätter en getter i en befintlig klassprototyp med en ny som omsluter den med en smart/själv-överskrivande/latare getter:
function cacheGetter<T>(ctor: { prototype: T }, prop: keyof T): void {
const desc = Object.getOwnPropertyDescriptor(ctor.prototype, prop);
if (!desc) throw new Error("OH NO, NO PROPERTY DESCRIPTOR");
const getter = desc.get;
if (!getter) throw new Error("OH NO, NOT A GETTER");
Object.defineProperty(ctor.prototype, prop, {
get() {
const ret = getter.call(this);
Object.defineProperty(this, prop, { value: ret });
return ret;
}
})
}
Så om du anropar cacheGetter(Clazz, "key") kommer det att hämta egenskapsbeskrivaren för Clazz.prototype.key, och se till att den har en getter. Om något av stegen misslyckas kastar det ett fel. Annars skapar det en ny getter som, när den anropas, anropar den ursprungliga gettern en gång på den nuvarande instansen (inte prototypen), och definierar sedan egenskapen direkt (återigen, inte prototypen) på instansen som det cachade värdet. Så nästa gång egenskapen nås på instansen används det cachade värdet på instansen och inte den ärvda gettern.
Låt oss testa det. Du applicerar det efter att klassen har deklarerats:
class CalendarDate extends Date {
get day() {
console.log('runs once');
return this.getDate();
}
}
cacheGetter(CalendarDate, "day"); // <-- here
Och se till att det fungerar som förväntat:
const c = new CalendarDate()
console.log(c.day); // runs once, 25
console.log(c.day); // 25
console.log(c.day); // 25
console.log(c.day); // 25
const d = new CalendarDate();
d.setDate(10);
console.log(d.day) // runs once, 10
console.log(c.day) // 25
console.log(d.day) // 10
Ser bra ut.
Så det är den kortaste metoden jag kan komma på för användare om du inte vill använda dekoratorer. Jag antar att dekoratormetoden skulle implementeras på ett liknande sätt, som ett omslag för egenskapsbeskrivaren.
Lekplatslänk till kod
