Javascript Memoizer淺析
以下來自John Hann的實(shí)現(xiàn),這段代碼引起了我的注意,它用巧妙的方法把方法調(diào)用的結(jié)果緩存起來了。
代碼解析:
// memoize: 使用memoization來緩存的通用方法
// func: 要被緩存的方法
// context: 方法執(zhí)行上下文
// Note: 方法必須是外部可訪問的,參數(shù)是可字符序列化的
function memoize (func, context) {
function memoizeArg (argPos) { //參數(shù)表示原始方法中參數(shù)的位置
var cache = {}; //這個(gè)緩存的key是參數(shù),value是執(zhí)行結(jié)果
return function () { //返回一個(gè)函數(shù)閉包
if (argPos == 0) { //第一個(gè)參數(shù),如果參數(shù)在緩存的key中不存在,就執(zhí)行原始函數(shù)并且存儲(chǔ)執(zhí)行結(jié)果
if (!(arguments[argPos] in cache)) {
cache[arguments[argPos]] = func.apply(context, arguments);
}
return cache[arguments[argPos]];
}
else { //不是第一個(gè)參數(shù),如果參數(shù)在緩存的key中不存在,就遞歸執(zhí)行memoizeArg方法,原始方法中參數(shù)的位置-1
if (!(arguments[argPos] in cache)) {
cache[arguments[argPos]] = memoizeArg(argPos - 1);
}
return cache[arguments[argPos]].apply(this, arguments);
}
}
}
var arity = func.arity || func.length; //func參數(shù)的長度,javascript中用length屬性,其它的用arity屬性
return memoizeArg(arity - 1); //從最后一個(gè)參數(shù)開始遞歸
}
使用:
var mem = memoize(func, this);
alert(mem.call(this,1,1,2));
alert(mem.call(this,2,1,2));
alert(mem.call(this,3,1,3));
alert(mem.call(this,2,2,4));
看似簡單,再一看好像也并不易懂,可是如果能對(duì)閉包的使用比較熟悉的話,就很好理解了。經(jīng)過上面幾次mem.call的調(diào)用之后,形成的是一棵樹,每個(gè)節(jié)點(diǎn)都是一個(gè)閉包,每個(gè)閉包內(nèi)有一個(gè)cache,每個(gè)cache的key都是樹分支:

(注:上面圖中的“結(jié)果”也是一個(gè)閉包,只不過argPos為0而已)
不過方法有諸多,比如limboy說:
function Memoize(fn){
var cache = {};
return function(){
var key = [];
for( var i=0, l = arguments.length; i < l; i++ )
key.push(arguments[i]);
if( !(key in cache) )
cache[key] = fn.apply(this, arguments);
return cache[key];
};
}
實(shí)現(xiàn)更簡易,不過把參數(shù)push到一個(gè)數(shù)組內(nèi),再把數(shù)組當(dāng)key,而key是只支持字符串型的,因此這點(diǎn)在使用上需要注意(比如一個(gè)對(duì)象tostring之后可能只看到”[object Object]“了),它的功能比上面那個(gè)要弱一些。
改進(jìn)這一點(diǎn)也不難,把參數(shù)另立一個(gè)對(duì)象即可,而原cache對(duì)象和這個(gè)另立的參數(shù)對(duì)象使用一個(gè)ID關(guān)聯(lián)起來:
function Memoize(fn){
var cache = {}, args = {};
return function(){
for( var i=0, key = args.length; i < key; i++ ) {
if( equal( args[i], arguments ) )
return cache[i];
}
args[key] = arguments;
cache[key] = fn.apply(this, arguments);
return cache[key];
};
}
還有一些其他的辦法,都可以寫成簡潔的函數(shù)式方法。
相關(guān)文章
Javascript計(jì)算二維數(shù)組重復(fù)值示例代碼
這篇文章主要給大家介紹了利用Javascript計(jì)算二維數(shù)組重復(fù)值的方法,文中給出了詳細(xì)的示例代碼,相信對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。2016-12-12
axios使用攔截器統(tǒng)一處理所有的http請(qǐng)求的方法
這篇文章主要介紹了axios使用攔截器統(tǒng)一處理所有的http請(qǐng)求的方法,通過一段實(shí)例代碼給大家介紹了axios攔截器使用,代碼簡單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-11-11
Javascript實(shí)現(xiàn)一朵從含苞到綻放的玫瑰
今天小編就為大家分享一篇關(guān)于Javascript實(shí)現(xiàn)一朵從含苞到綻放的玫瑰,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03
JavaScript獲取數(shù)組最后一個(gè)元素的3種方法以及性能
在開發(fā)過程中,我們常常需要得到j(luò)s數(shù)組的最后一個(gè)數(shù)組元素,下面這篇文章主要給大家介紹了關(guān)于JavaScript獲取數(shù)組最后一個(gè)元素的3種方法以及性能,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06
js實(shí)現(xiàn)獲取最新本周周一開始的日期(單周日歷卡)
這篇文章主要為大家介紹了js實(shí)現(xiàn)獲取最新本周周一開始的日期(單周日歷卡)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10
微信小程序?qū)崿F(xiàn)展示評(píng)分結(jié)果功能
這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)展示評(píng)分結(jié)果功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-02-02
Javascript this 的一些學(xué)習(xí)總結(jié)
相信有C++、C#或Java等編程經(jīng)驗(yàn)的各位,對(duì)于this關(guān)鍵字再熟悉不過了。由于Javascript是一種面向?qū)ο蟮木幊陶Z言,它和C++、C#或Java一樣都包含this關(guān)鍵字,接下來我們將向大家介紹Javascript中的this關(guān)鍵字2012-08-08
js獲取TreeView控件選中節(jié)點(diǎn)的Text和Value值的方法
在實(shí)際項(xiàng)目中,遇到一個(gè)問題,首先彈出一個(gè)新窗口,新窗口中放了一個(gè)TreeView控件,現(xiàn)在要解決的是,如何單擊TreeView中一個(gè)節(jié)點(diǎn),返回Text和Value到父頁面并關(guān)閉該新窗口,本文將詳細(xì)介紹此方法的實(shí)現(xiàn)2012-11-11
JavaScript使用localStorage判斷設(shè)置值是否過期
本文主要介紹了JavaScript使用localStorage判斷設(shè)置值是否過期,通過設(shè)置過期時(shí)間,我們可以使用 setItemWithExpiration 函數(shù)將數(shù)據(jù)存儲(chǔ)到 localStorage 中,并使用 getItemWithExpiration 函數(shù)獲取數(shù)據(jù)并檢查是否過期,感興趣的可以了解一下2023-05-05

