使用JavaScrip實現(xiàn)一個記憶函數(shù)
說在前面
在編程的世界里,性能優(yōu)化始終是一個重要的話題。今天,我們將一起來實現(xiàn)一個實用的記憶函數(shù)(簡單來說,就是同樣的入?yún)?,只會在第一次調(diào)用指定函數(shù)獲取結(jié)果,后續(xù)則可以直接獲取到第一次計算的結(jié)果返回),它能夠顯著提升函數(shù)調(diào)用的效率,特別是在處理重復(fù)計算的場景中。
需求
現(xiàn)給定一個函數(shù) fn ,返回該函數(shù)的一個 記憶化 版本。
一個 記憶化 的函數(shù)是一個函數(shù),它不會被相同的輸入調(diào)用兩次。而是會返回一個緩存的值。
函數(shù) fn 可以是任何函數(shù),對它所接受的值類型沒有任何限制。如果兩個輸入值在 JavaScript 中使用 === 運算符比較時相等,則它們被視為相同。
示例1
輸入:
getInputs = () => [[2,2],[2,2],[1,2]]
fn = function (a, b) { return a + b; }
輸出:
[{"val":4,"calls":1},{"val":4,"calls":1},{"val":3,"calls":2}]
解釋:
const inputs = getInputs();
const memoized = memoize(fn);
for (const arr of inputs) {
memoized(...arr);
}
對于參數(shù)為 (2, 2) 的輸入: 2 + 2 = 4,需要調(diào)用 fn() 。
對于參數(shù)為 (2, 2) 的輸入: 2 + 2 = 4,這些輸入之前已經(jīng)出現(xiàn)過,因此不需要再次調(diào)用 fn()。
對于參數(shù)為 (1, 2) 的輸入: 1 + 2 = 3,需要再次調(diào)用 fn(),總共調(diào)用了 2 次。
示例2
輸入:
getInputs = () => [[{},{}],[{},{}],[{},{}]]
fn = function (a, b) { return a + b; }輸出:
[{"val":{},"calls":1...
解釋:
將兩個空對象合并總是會得到一個空對象。盡管看起來應(yīng)該緩存命中并只調(diào)用一次 fn(),但是這些空對象彼此之間都不是 === 相等的。
示例3
輸入:
getInputs = () => { const o = {}; return [[o,o],[o,o],[o,o]]; }
fn = function (a, b) { return ({...a, ...b}); }
輸出:
[{"val":{},"calls":1},{"val":{},"calls":1},{"val":{},"calls":1}]
解釋:
將兩個空對象合并總是會得到一個空對象。因為傳入的每個對象都是相同的,所以第二個和第三個函數(shù)調(diào)用都會命中緩存。
代碼實現(xiàn)
1、入?yún)⑻幚?/h3>
要避免重復(fù)計算相同入?yún)ⅲ俏覀兙托枰獙⒚看斡嬎愕娜雲(yún)⒂涗浧饋?,但是入?yún)卸鄠€,我們還需要將所有入?yún)⒁灿涗浧饋?,那么怎么將多個入?yún)⑥D(zhuǎn)成一個key呢?
首先我們先簡化一下,如果入?yún)⒍际亲址脑捨覀儠趺催M行處理?

沒錯,都是字符串的話我們可以直接使用連接符號將所有入?yún)⑦B接起來作為一個key。那么現(xiàn)在入?yún)?shù)據(jù)格式多種的情況我們應(yīng)該要怎么處理呢?
既然入?yún)⒍际亲址臅r候我們可以處理,那我們就給每一個入?yún)①x予一個專屬的id,如下圖:

const idMap = new Map();
const getId = (k) => {
if (idMap.has(k)) {
return idMap.get(k);
}
const size = idMap.size;
idMap.set(k, size);
return size;
};
這個函數(shù)用于為傳入的參數(shù)生成一個唯一的標(biāo)識符。它首先檢查 idMap 中是否已經(jīng)存在該參數(shù)對應(yīng)的標(biāo)識符,如果存在,則直接返回;如果不存在,就將當(dāng)前 idMap 的大小作為新的標(biāo)識符,并將參數(shù)與標(biāo)識符的映射關(guān)系存儲到 idMap 中。
因為這里的入?yún)⒖梢允侨我忸愋偷臄?shù)據(jù),所以我們這里不能直接用Object對象來記錄,可以使用Map來記錄。
const a = {aa:1};
const b = {};
const c = new Map();
b[a] = 1;
c.set(a,1);
console.log(b);
console.log(c);
這里我們得到的b為:
{
"[object Object]": 1
}
c為:
new Map([
[
{
"aa": 1
},
1
]
])

2、記錄入?yún)?/h3>
遍歷arguments,獲取到每一個入?yún)⒌膶賗d,最后使用-將所有id連接起來作為一組入?yún)⒌?code>key,將計算得到的值作為value保存。
const arr = [];
for (const element of arguments) {
arr.push(getId(element));
}
const key = arr.join("-");
if (!valMap.has(key)) {
valMap.set(key, fn(...arguments));
}
return valMap.get(key);
3、完整代碼
/**
* @param {Function} fn
* @return {Function}
*/
function memoize(fn) {
const idMap = new Map();
const valMap = new Map();
const getId = (k) => {
if (idMap.has(k)) {
return idMap.get(k);
}
const size = idMap.size;
idMap.set(k, size);
return size;
};
return function () {
const arr = [];
for (const element of arguments) {
arr.push(getId(element));
}
const key = arr.join("-");
if (!valMap.has(key)) {
valMap.set(key, fn(...arguments));
}
return valMap.get(key);
};
}
應(yīng)用場景
假設(shè)我們有一個復(fù)雜的對象,其中某個屬性的計算需要消耗大量資源,并且該屬性可能會被多次訪問。
const complexObject = {
data: {
// 大量復(fù)雜數(shù)據(jù)
},
getComputedProperty: memoize(function () {
// 復(fù)雜的計算邏輯,例如遍歷 data 中的數(shù)據(jù)進行統(tǒng)計分析
return result;
})
};
console.log(complexObject.getComputedProperty());
console.log(complexObject.getComputedProperty());
在第一次訪問 complexObject.getComputedProperty 時,會執(zhí)行復(fù)雜的計算邏輯并緩存結(jié)果。后續(xù)再次訪問時,直接返回緩存的結(jié)果,避免了重復(fù)計算,提高了整體性能。
到此這篇關(guān)于使用JavaScrip實現(xiàn)一個記憶函數(shù)的文章就介紹到這了,更多相關(guān)JavaScrip函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談layui使用模板引擎動態(tài)渲染元素要注意的問題
今天小編就為大家分享一篇淺談layui使用模板引擎動態(tài)渲染元素要注意的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09
javascript實現(xiàn)刪除前彈出確認(rèn)框
刪除確認(rèn)對話框的JS代碼,有好幾種寫法,有簡單的,有兼容好的,下面分別說幾種方法,可根據(jù)自己需要選用2015-06-06
JS中append字符串包含onclick無效傳遞參數(shù)失敗的解決方案
這篇文章主要介紹了JS中append字符串包含onclick無效傳遞參數(shù)失敗的解決方案,需要的朋友可以參考下2016-12-12
JavaScript canvas實現(xiàn)七彩時鐘效果
這篇文章主要為大家詳細介紹了JavaScript canvas實現(xiàn)七彩時鐘效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-05-05
JS實現(xiàn)簡單的星期格式轉(zhuǎn)換功能示例
這篇文章主要介紹了JS實現(xiàn)簡單的星期格式轉(zhuǎn)換功能,涉及JavaScript基于Date()對象的日期時間操作技巧,需要的朋友可以參考下2018-07-07
基于JavaScript實現(xiàn)抽獎系統(tǒng)
這篇文章主要為大家詳細介紹了基于JavaScript實現(xiàn)抽獎系統(tǒng),具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-01-01

