函数可以将先前操作的结果记录在某个对象里,从而避免无谓的重复计算。这种优化被称为记忆(memoization)。
以递归函数计算斐波那契数列(一个 Fibonacci 数字为前两个数字之和,最前面的两个数字为0和1)为例:
var fibonacci = function (n) {
return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
}
如果计算量变大,上面的函数会做很多无谓的工作:
// 下面的代码调用了 fibonacci 函数453次
for (var i = 0; i <= 10; i++) {
fibonacci(i);
}
优化:我们可以在一个名为 memo 的数组里保存我们的存储结果,它可以隐藏在闭包中。当函数调用时,先检查结果是否已经存在,如果存在就立即返回这个结果:
var fibonacci = function (n) {
var memo = [0, 1];
var handle = function (n) {
var result = memo[n];
if (typeof result !== 'number') {
result = handle(n - 1) + handle(n - 2);
}
return result;
};
return handle;
}(); // 注意结尾的括号!!!
举一反三:我们可以推而广之,编写一个函数来帮助我们构造带记忆功能的函数。
// memo:初始值数组
// formula:返回计算公式的回调函数
var memoizer = function (memo, formula) {
var recur = function (n) {
var result = memo[n];
if (typeof result !== 'number') {
result = formula(recur, n);
memo[n] = result;
}
return result;
};
return recur;
};
memoizer 函数取得一个初始的 memo 数组和 formula 函数,它返回一个管理 momo 存储和在需要时调用 formula 函数的 recur 函数;
如果使用 memoizer 函数来定义 fibonacci 函数:
var fibonacci = memoizer([0, 1], function (recur, n) {
return recur(n - 1) + recur(n - 2);
});
通过设计这种产生另一个函数的函数,极大的减少了工作量,只需要专注于初始状态和基本公式的输入即可。