函数可以使用对象去记住先前操作的结果,从而避免多余的运算。
比如我们现在测试一个费波纳茨算法,我们可以使用递归函数计算fibonacci数列,一个fibonacci数字是之前两个fibonacci数字之和,最前面的两个数字是0和1。代码如下:
var count = 0; //记录函数调用次数
var fibonacci = function(n) {
count++;
return n < 2 ? n : fibonacci(n-1) + fibonacci(n-2);
};
for(var i = 0; i <= 10; i+=1) {
console.log(i+":"+fibonacci(i));
}
console.log(count); // 453
我们可以看到如上 fibonacci函数总共调用了453次,for循环了11次,它自身调用了442次,如果我们使用下面的记忆函数的话,那么就可以减少他们的运算次数,从而提高性能。
思路:先使用一个临时数组保存存储结果,当函数被调用的时候,先查找是否已经有存储结果,如果有的话,就立即返回这个存储结果,没有的话,调用函数进行运算。代码如下:
1 var count2 = 0;
2 var fibonacci2 = (function(){
3 var memo = [0,1];
4 var fib = function(n) {
5 var result = memo[n];
6 count2++;
7 if(typeof result !== 'number') {
8 result = fib(n-1) + fib(n-2);
9 memo[n] = result;
10 }
11 return result;
12 };
13 return fib;
14 })();
15 for(var j = 0; j <= 10; j+=1) {
16 console.log(j+":"+fibonacci2(j));
17 }
18 console.log(count2); // 29
这个函数也返回了同样的结果,但是只调用了函数29次,循环了11次,也就是说函数自身调用了18次,从而减少无谓的函数的调用及运算,下面我们可以把这个函数进行抽象化,以构造带记忆功能的函数,如下代码:
1 var count3 = 0;
2 var memoizer = function(memo,formula) {
3 var recur = function(n) {
4 var result = memo[n];
5 count3++; // 这句代码只是说明运行函数多少次,在代码中并无作用,实际使用上可以删掉
6 if(typeof result !== 'number') {
7 result = formula(recur,n);
8 memo[n] = result;
9 }
10 return result;
11 };
12 return recur;
13 };
14 var fibonacci3 = memoizer([0,1],function(recur,n){
15 return recur(n-1) + recur(n-2);
16 });
17 // 调用方式如下
18 for(var k = 0; k <=10; k+=1) {
19 console.log(k+":"+fibonacci3(k));
20 }
21 console.log(count3); // 29
如上封装 memoizer 里面的参数是实现某个方法的计算公式,具体的可以根据需要自己手动更改,这里的思路无非就是想习惯使用对象去保存临时值,从而减少不必要的取值存储值的操作;
学习资源:理解使用函数实现历史记录--提高性能