這是Method decorator的衍生產品,它只允許執行一次被修飾的方法 Typescript。
我get在打字稿類上有一些計算量很大的方法。對于其中一些,我可以確定每次結果都是相同的——它不會根據類實體的狀態而改變。但是,該方法在代碼中的單個實體上被多次呼叫。在這方面,只在第一次實際運行計算而不是在隨后的時間運行計算是有意義的。例如:
class Cell {
angle: number;
count: number;
private _cosine?: number;
constructor(angle) {
this.angle = angle;
this.count = 0;
}
get cosine() {
if (this.count) return this._cosine;
this._cosine = Math.cos(this.angle);
this.count ;
return this._cosine;
}
}
const cells = Array.from({ length: 100 }).map(
(_, i) => new Cell(i * 180 * Math.PI)
);
cells.forEach((cell) => {
for (i = 0; i < 100; i ) {
const cosine = cell.cosine;
}
});
第一次cell.cosine訪問時,它實際上運行計算量大的代碼并將結果分配給私有屬性_cosine。隨后的每一次,它都會簡單地回傳該值。如果您運行此代碼,您將看到.countany 的cell值僅為 1,即使cell.cosine每個實體被訪問 100 次。
裝修師傅能做到嗎?
這樣做的缺點是,對于我希望這個一次性邏輯應用到的每個屬性,我需要宣告 a private _property,并包含一點點邏輯。裝飾方法使其只運行一次的問題有一個很好的答案。用戶 plumbn 對此進行了調整以與 getter 一起使用。但是,該答案不適用于 getter/accessor 這種情況,因為 getter每次呼叫時都需要回傳一個值。
是否可以為 get 方法撰寫一個裝飾器,使其僅在第一次呼叫時真正運行(并回傳值),而在隨后的時間里,它回傳最初計算的值而不再次運行計算?
我知道你在想什么...
你可能會問“為什么不在建構式中賦值呢?” 這是一個很好的問題。如果可以的話,你應該這樣做,因為它在性能上的巨大改進,正如這個 jsbench 顯示的那樣。然而,除了丑陋之外,在某些情況下這也行不通。例如
class Cell {
angle: position;
count: number;
constructor(position) {
this.position = position;
this.count = 0;
}
get neighbors() {
let neighbors = [];
for (let j = -1; j <= 1; j ) {
for (let i = -1; i <= 1; i ) {
neighbors.push(
new Cell([x i, y j]),
);
}
}
return neighbors;
}
}
在這種情況下,呼叫一個cell.neighbor方法會產生 8 個新單元。如果我去分配this.neighbors在建構式中,那么每個新生成的細胞會呼叫this.neighbors在其建構式中,我們已經有了一個無限回圈。在這種情況下,cell.neighbors每次訪問它時都回傳相同的值,但是每次呼叫它時都運行該代碼是一種浪費。我可以使用一種if (this._neighbors) { return this._neighbors } else { this._neighbors = run_my_code(); return this._neighbors }模式,但在裝飾器中捕捉這種行為似乎更優雅。我在上面發布的 jsbench 顯示了在執行此操作時性能略有提高(不如在建構式中宣告它,但仍然是一個改進)。
uj5u.com熱心網友回復:
我相信我們可以第一次計算該值并存盤它,然后使用reflect-metadata如下方法在后續呼叫中檢索它。
import "reflect-metadata";
const metadataKey = Symbol("initialized");
function once(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const getter = descriptor.get!;
descriptor.get = function () {
const val = Reflect.getMetadata(metadataKey, target, propertyKey);
if (val) {
return val;
}
const newValue = getter.apply(this);
Reflect.defineMetadata(metadataKey, newValue, target, propertyKey);
return newValue;
};
}
可以在此處找到一個作業示例。
非常感謝 OP 幫助解決了這些問題。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/409694.html
標籤:
