underscore.js 中有一个 memoize 方法:
以下是代码片段: // Memoize an expensive function by storing its results. _.memoize = function(func, hasher) { var memo = {}; hasher = hasher || _.identity; return function() { var key = hasher.apply(this, arguments); return key in memo ? memo[key] : (memo[key] = func.apply(this, arguments)); }; }; |
- 当 arguments 不是简单字符串时,如何有效构建 hasher 来生成唯一 key 值;
- 函数值的存储和获取;
- 当缓存数据很大时,如何根据访问频率来释放低访问量的缓存项。
第一个和第三个问题这里不谈。对于第二个问题,一般的实现方法是采用数组或对象来保存。比如 underscore.js 中的实现里,采用的是 memo = {}
和 memo[key]
来实现存储和获取,这时要注意一个陷阱:
返回的值是
test('toString')
以下是代码片段:
var test = _.memoize(function(str) {
return str;
});
alert(test(’hello’) == ’hello’); // true
alert(test(’toString’) == ’toString’); // false({}).toString.toString()
, 这是由 key in memo
导致的。立刻修改为:
以下是代码片段: // snip... return memo.hasOwnProperty(key) ? memo[key] : (memo[key] = func.apply(this, arguments)); // snip... |
以下是代码片段: test(’hasOwnProperty’); test(’throw exception’); |
memo.hasOwnPropery
已经不指向 Object.prototype.hasOwnProperty
. 知道了问题所在,修改就不难了:
以下是代码片段: // Memoize an expensive function by storing its results. _.memoize = function(func, hasher) { var memo = {}; hasher = hasher || _.identity; return function() { var key = hasher.apply(this, arguments); return Object.prototype.hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); }; }; |