JavaScript中延遲加載屬性
點擊上方關注 TianTianUp,一起學習,天天進步
大家好,我是TianTian。
今天分享的內(nèi)容是JavaScript中延遲加載屬性模式。
內(nèi)容來自外網(wǎng),看完覺得挺有意思,做個分享。
改善性能的最好方法之一是避免重復兩次相同的工作。
因此,只要可以緩存結果供以后使用,就可以加快程序的速度。
諸如延遲加載屬性模式之類的技術使任何屬性都可以成為緩存層以提高性能。
這里說到的延遲加載屬性模式就是利用的訪問器屬性,將計算昂貴的操作推遲到需要時再使用。
場景
有些時候,你會在JavaScript類內(nèi)部創(chuàng)建一些屬性,它保存實例中可能需要的任何數(shù)據(jù)。
對于在構造函數(shù)內(nèi)部隨時可用的小數(shù)據(jù)而言,這不是問題。
但是,如果需要在實例中可用之前計算一些大數(shù)據(jù),則您可能需要執(zhí)行昂貴的計算操作。例如,考慮此類:
class MyClass {
constructor() {
this.data = someExpensiveComputation();
}
}
在這里,該data屬性是執(zhí)行一些昂貴的計算而創(chuàng)建的。
如果您不確定將使用該屬性,則提前執(zhí)行可能不太好,效率低。幸運的是,接下來介紹幾種方法可以將這些操作推遲。
接下來主要圍繞的訪問器屬性來展開的。
按需屬性模式
優(yōu)化執(zhí)行計算操作的最簡單方法是等到需要數(shù)據(jù)后再進行計算。
例如,您可以使用帶有getter的data屬性來按需進行計算,如下所示:
class MyClass {
get data() {
return someExpensiveComputation();
}
}
在這種情況下,直到有人第一次讀取該data屬性時,您的昂貴的計算操作才發(fā)生,這是一種改進。
但是,也是存在問題的,每次data讀取屬性時都會執(zhí)行相同的昂貴計算操作,這比之前的示例(其中至少僅執(zhí)行一次計算)差。
按照我們分析的情況來看,這不是一個好的解決方案,所以可以在此基礎上創(chuàng)建一個更好的解決方案。
延遲加載屬性模式
只有在訪問該屬性時才執(zhí)行計算是一個好的開始。您真正需要的是在那之后緩存信息,然后僅使用該緩存的數(shù)據(jù)結果。
但是,有個問題需要我們考慮,您將這些信息緩存在何處以便于訪問呢?
最簡單的方法是定義一個具有相同名稱的屬性,并將其值設置為計算出的數(shù)據(jù),如下所示:
class MyClass {
get data() {
const actualData = someExpensiveComputation();
Object.defineProperty(this, "data", {
value: actualData,
writable: false,
configurable: false,
enumerable: false
});
return actualData;
}
}
在這里,該data屬性再次被定義為該類的getter,但是這一次它將緩存結果。
調(diào)用Object.defineProperty()創(chuàng)建一個名為的新屬性data,該屬性的固定值為actualData,并且被設置為不可寫,不可配置和可枚舉。
下次data訪問該屬性時,它將從新創(chuàng)建的屬性中讀取而不是調(diào)用getter:
const object = new MyClass();
// calls the getter
const data1 = object.data;
// reads from the data property
const data2 = object.data;
實際上,所有計算僅在第一次讀取數(shù)據(jù)屬性時完成。數(shù)據(jù)屬性的每次后續(xù)讀取都將返回緩存的版本。這種模式的缺點是data屬性開始時是不可枚舉的原型屬性,最后是不可枚舉的自己的屬性:
const object = new MyClass();
console.log(object.hasOwnProperty("data")); // false
const data = object.data;
console.log(object.hasOwnProperty("data")); // true
盡管這種區(qū)別在許多情況下并不重要,但了解這種模式很重要,因為在傳遞對象時,這種模式可能會引起細微的問題。
幸運的是,我們可以使用接下來的模式很容易解決這個問題。
類的延遲加載屬性
如果您有一個實例,對于這個實例,延遲加載屬性存在很重要,那么您可以使用Object.defineProperty()在類構造函數(shù)內(nèi)部創(chuàng)建該屬性。
它比前面的示例有點混亂,但是它將確保該屬性僅存在于實例上。這是一個例子:
class MyClass {
constructor() {
Object.defineProperty(this, "data", {
get() {
const actualData = someExpensiveComputation();
Object.defineProperty(this, "data", {
value: actualData,
writable: false,
configurable: false
});
return actualData;
},
configurable: true,
enumerable: true
});
}
}
我們從這個例子中可以發(fā)現(xiàn),構造函數(shù)使用創(chuàng)建data訪問器屬性Object.defineProperty()。該屬性是在實例上創(chuàng)建的(使用this),定義了一個getter并指定了可枚舉和可配置的屬性。
將data屬性設置為可配置尤其重要,這樣您可以Object.defineProperty()再次調(diào)用它。
然后,getter函數(shù)進行計算并再次調(diào)用Object.defineProperty()。對于data來說,將該屬性重新定義為具有特定值的數(shù)據(jù)屬性,并且將其變?yōu)椴豢蓪懬也豢膳渲靡员Wo最終數(shù)據(jù)。下次data讀取該屬性時,將從存儲的值中讀取該屬性。該data屬性現(xiàn)在僅作為自己的屬性存在,并且在第一次讀取之前和之后都具有相同的作用:
const object = new MyClass();
console.log(object.hasOwnProperty("data")); // true
const data = object.data;
console.log(object.hasOwnProperty("data")); // true
對于類,這很可能是您要使用的模式。另一方面,對象模式下可以使用更簡單的方法。
對象的延遲加載屬性
如果使用的是對象模式而不是類,則過程要簡單得多,因為在對象模式上定義的getter與數(shù)據(jù)屬性一樣被定義為可枚舉的自身屬性(而不是原型屬性)。這意味著您可以為類使用延遲加載屬性模式,而不會造成混亂:
const object = {
get data() {
const actualData = someExpensiveComputation();
Object.defineProperty(this, "data", {
value: actualData,
writable: false,
configurable: false,
enumerable: false
});
return actualData;
}
};
console.log(object.hasOwnProperty("data")); // true
const data = object.data;
console.log(object.hasOwnProperty("data")); // true
總結
通過從重新定義為數(shù)據(jù)屬性的訪問器屬性開始,您可以將計算推遲到第一次讀取該屬性時,然后將結果緩存起來以備后用。這種方法適用于類和對象文字,并且在對象模式中更簡單一些,因為您不必擔心getter最終會出現(xiàn)在原型上。
最后
面試交流群持續(xù)開放,分享了近 許多 個面經(jīng)
加微信: DayDay2021,備注面試,拉你進群
我是 TianTian,我們下篇見~

