使用JavaScrip實(shí)現(xiàn)一個記憶函數(shù)
說在前面
在編程的世界里,性能優(yōu)化始終是一個重要的話題。今天,我們將一起來實(shí)現(xiàn)一個實(shí)用的記憶函數(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 中使用 === 運(yùn)算符比較時相等,則它們被視為相同。
示例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}]
解釋:
將兩個空對象合并總是會得到一個空對象。因?yàn)閭魅氲拿總€對象都是相同的,所以第二個和第三個函數(shù)調(diào)用都會命中緩存。
代碼實(shí)現(xiàn)
1、入?yún)⑻幚?/h3>
要避免重復(fù)計算相同入?yún)?,那我們就需要將每次計算的入?yún)⒂涗浧饋?,但是入?yún)卸鄠€,我們還需要將所有入?yún)⒁灿涗浧饋?,那么怎么將多個入?yún)⑥D(zhuǎn)成一個key呢?
首先我們先簡化一下,如果入?yún)⒍际亲址脑捨覀儠趺催M(jìn)行處理?

沒錯,都是字符串的話我們可以直接使用連接符號將所有入?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)檫@里的入?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ù)進(jìn)行統(tǒng)計分析
return result;
})
};
console.log(complexObject.getComputedProperty());
console.log(complexObject.getComputedProperty());
在第一次訪問 complexObject.getComputedProperty 時,會執(zhí)行復(fù)雜的計算邏輯并緩存結(jié)果。后續(xù)再次訪問時,直接返回緩存的結(jié)果,避免了重復(fù)計算,提高了整體性能。
到此這篇關(guān)于使用JavaScrip實(shí)現(xiàn)一個記憶函數(shù)的文章就介紹到這了,更多相關(guān)JavaScrip函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
概述如何實(shí)現(xiàn)一個簡單的瀏覽器端js模塊加載器
本文主要對實(shí)現(xiàn)一個簡單的js加載器的步驟進(jìn)行介紹--主要可以分為解析路徑、下載模塊、解析模塊依賴、解析模塊四個步驟。需要的朋友來看下吧2016-12-12
微信小程序 this.triggerEvent()的具體使用
這篇文章主要介紹了微信小程序 this.triggerEvent()的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
JavaScript 申明函數(shù)的三種方法 每個函數(shù)就是一個對象(一)
JavaScript 申明函數(shù)的三種方法 每個函數(shù)就是一個對象(一)2009-12-12
微信小程序?qū)崿F(xiàn)多行文字超出部分省略號顯示功能
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)多行文字 超出部分省略號顯示功能,比如設(shè)置只顯示2行,超出部分省略號顯示,本文通過實(shí)例代碼給大家介紹,需要的朋友可以參考下2019-10-10
JavaScript中的for...of和for...in循環(huán)容易遇到的問題及解決方法總結(jié)
在 JavaScript 編程中,for...of 和 for...in 是常用的循環(huán)語法,但它們在使用時可能會引發(fā)一些意想不到的問題,本文將分享我在使用這兩種循環(huán)時所遇到的坑和經(jīng)驗(yàn),需要的朋友可以參考下2023-08-08
理解JAVASCRIPT中hasOwnProperty()的作用
JavaScript中hasOwnProperty函數(shù)方法是返回一個布爾值,指出一個對象是否具有指定名稱的屬性2013-06-06

