在JavaScript的函数中利用闭包实现“记忆”功能

作者:admin     字体:[增加 减小]    类型:原创
在JavaScript语言中如何让函数实现“记忆功能”呢?是不是还要写一个类?其实我们可以使用闭包这样的编程技巧来实现。

在JavaScript语言中如何让函数实现记忆功能呢?这里说的记忆功能,是将函数上次的计算结果缓存起来。

其实,记忆只是一种编程技巧,本质上是牺牲算法的空间复杂度以换取更优的时间复杂度,在客户端JavaScript中代码的执行时间复杂度往往成为瓶颈,因在在大多数场景下,这种牺牲空间换取时间的做法以提升程序执行效率的做法是非常可取的。

比如下面的代码,利用闭包实现了对阶乘结果的“记忆功能”

//计算阶乘,并将结果缓存至函数的属性中
var factorial = (function(){
    var cache=[];
    cache[1] = 1;
    return function(n){
        if ( isFinite(n) && n>0 && n==Math.round(n)) {
            if ( !(n in cache) ) {
                //采用了arguments.callee.call()来调用所在匿名函数
                cache[n] = n * arguments.callee.call(this,n-1);
            } else {
                //为了演示,调用cache时,我们都在控制器中输出一下
                console.log('从自身调用了cache[' + n + ']');
            }
            return cache[n];
        } else {
            return NaN;
        }
    }
}());

console.log(factorial(1));  //1,从自身调用了cache[1]
console.log(factorial(2));  //2,从自身调用了cache[1]
console.log(factorial(5));  //120,从自身调用了cache[2]
console.log(factorial(10)); //3628800,从自身调用了cache[5]

为了达到通用的效果,我们从中提炼出一个函数memorize(),代码如下

function memorize(f) {
    var cache = []; //将函数返回值保存在闭包里
    return function() {
        var key = arguments.length + Array.prototype.join.call(arguments,',');
        if (key in cache) return cache[key];
        else return cache[key] = f.apply(this,arguments);       
    };
}

同样是阶乘函数,我们可以这样简单实现一下

//注意,当我们写一个递归函数时,往往需要实现记忆功能
//我们更希望调用实现了记忆功能的递归函数,而不是原递归函数
var factorial = memorize(function(n){
    return n<=1 ? 1 : n*factorial(n-1);
});

console.log(factorial(5));  //120,对于4~1的值也有缓存

对于这样的记忆功能,在网站的后台数据查询时,非常有用。