以下來自John Hann的實現,這段代碼引起了我的注意,它用巧妙的方法把方法調用的結果緩存起來了。
代碼解析:
復制代碼 代碼如下:
// memoize: 使用memoization來緩存的通用方法
// func: 要被緩存的方法
// context: 方法執行上下文
// Note: 方法必須是外部可訪問的,參數是可字符序列化的
function memoize (func, context) {
function memoizeArg (argPos) { //參數表示原始方法中參數的位置
var cache = {}; //這個緩存的key是參數,value是執行結果
return function () { //返回一個函數閉包
if (argPos == 0) { //第一個參數,如果參數在緩存的key中不存在,就執行原始函數並且存儲執行結果
if (!(arguments[argPos] in cache)) {
cache[arguments[argPos]] = func.apply(context, arguments);
}
return cache[arguments[argPos]];
}
else { //不是第一個參數,如果參數在緩存的key中不存在,就遞歸執行memoizeArg方法,原始方法中參數的位置-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參數的長度,javascript中用length屬性,其它的用arity屬性
return memoizeArg(arity - 1); //從最後一個參數開始遞歸
}
使用:
復制代碼 代碼如下:
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));
看似簡單,再一看好像也並不易懂,可是如果能對閉包的使用比較熟悉的話,就很好理解了。經過上面幾次mem.call的調用之後,形成的是一棵樹,每個節點都是一個閉包,每個閉包內有一個cache,每個cache的key都是樹分支:
(注:上面圖中的“結果”也是一個閉包,只不過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];
};
}
實現更簡易,不過把參數push到一個數組內,再把數組當key,而key是只支持字符串型的,因此這點在使用上需要注意(比如一個對象tostring之後可能只看到”[object Object]“了),它的功能比上面那個要弱一些。
改進這一點也不難,把參數另立一個對象即可,而原cache對象和這個另立的參數對象使用一個ID關聯起來:
復制代碼 代碼如下:
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];
};
}
還有一些其他的辦法,都可以寫成簡潔的函數式方法。