前端记忆函数和LRU缓存

在Js中,“记忆化(Memoization)”是一种优化技术,它通过存储昂贵函数的结果,并复用这些结果以避免重复执行,从而可以加快代码执行速度。这种技术在处理递归和迭代问题时尤其有用。

下面是一个记忆化函数的一般实现:

function memoize(fn){
    let cache = {}
    return function(...args){
        let n = args[0]
        if ( n in cache ) {
            console.log('Fetching from cache');
            return cache[n]
        }
        else {
            console.log('Calculating result');
            let result = fn(n)
            cache[n] = result;
            return result;
        }
    }
}

// 例如: 我们有一个计算斐波那契数列的函数:
var fib = function(n) {
    if (n < 2){
      return n
    }
    return fib(n - 1) + fib(n - 2)
}

// 使用记忆函数优化 fib 函数
fib = memoize(fib);

// 则下次我们调用 fib 函数时,当碰到重复计算的数就会直接从缓存中获取值,避免了重复计算。

这个 memoize 函数利用了JavaScript中对象字典的特性以及闭包的特性。存储在 cache 对象中的值可以在同一个函数中持久存在,可以随时被访问和更改。

记忆函数中缓存的有效期
可以添加一个缓存过期机制来设定有效时间,这样可以防止缓存无限量增长,或者处理那些可能会随时间而改变的数据。

具体的实现方式可能会有很多种,下面是一种简单的示例:

function memoize(func, ttl) {
    let cache = {};

    return (...args) => {
        let key = JSON.stringify(args);

        if(key in cache && Date.now() - cache[key].time < ttl){
            return cache[key].value;
        } else {
            let value = func(...args);
            cache[key] = { value: value, time: Date.now() }; // 存储计算结果以及当前的时间戳
            return value;
        }
    };
}

在这个例子中,memoize 函数的第二个参数 ttl 代表缓存的有效时间(以毫秒为单位)。当我们从缓存中获取数据时,我们会检查数据的时间戳是否还在有效期内,如果是,我们就返回该值;否则,我们就重新计算结果,并存储新的值和当前的时间戳。

使用 LRU(Least Recently Used)算法来实现一个有容量限制的缓存,这样可以在缓存满了之后自动淘汰最久未使用的数据

// 创建一个新的LRU缓存实例
function LRUCache(capacity) {
    this.capacity = capacity;
    this.cache = new Map();
}

// 获取键值对应的缓存值
LRUCache.prototype.get = function(key) {
    if(this.cache.has(key)) {
        // 移除并重新添加键值对,保持该键为最近使用
        let temp = this.cache.get(key);
        this.cache.delete(key);
        this.cache.set(key, temp);
        return temp;
    } else {
        // 如果键不存在,则返回-1
        return -1;
    }
};

// 添加/更新缓存
LRUCache.prototype.put = function(key, value) {
    if(this.cache.has(key)) {
        // 存在则先移除
        this.cache.delete(key);
    } else if(this.cache.size >= this.capacity) {
        // 缓存容量已满,移除最早加入的键值
        this.cache.delete(this.cache.keys().next().value);
    }
    this.cache.set(key, value);
};

LRUCache 实现了两个主要方法,get 和 put。get 方法用于获取存在的值,或当键不存在时返回 -1。如果获取到了值,就会移动当前的键值对到最近使用的位置。put 方法可以插入新的键值对,如果键已经存在,在添加新的键值对之前,要先删除老的键值对。如果缓存已满,就会删除最早(最少使用)的键值对。通过这种方式,缓存将始终维持在设置的容量以下。

LRU缓存是按照以下规则删除最早加入的键值对

LRU(Least Recently Used) 缓存的工作原理基于一个观察,即最近使用过的数据在未来很可能会再次被使用。因此,当空间满了,并且需要添加新的键值对时,应当去掉已有缓存中最久未被使用的数据。

更具体来说,每当在缓存中查询一个键值对时(无论是获取还是存储),都将其移至缓存队列的顶端。这样,最近被使用的键值对总是在队伍的顶端,而最久未被使用的键值对则总是在队伍的底端。

当添加新的键值对而缓存已满时,缓存将从底端删除最久未被使用的键值对,从而为新的键值对腾出空间。这就是LRU缓存的基本工作原理。

在JavaScript中,可以使用Map对象来实现这个功能,因为Map按照插入顺序来迭代元素 ---- 最新插入的元素在最后,最早插入的元素在最前。当删除并重新插入已有的键值对时,该键值对会自动被移到最后。这正是我们想要的行为:最近使用的数据始终在最后,最久未被使用的数据在最前。

因此,当LRU缓存需要删除最早的键值对以腾出空间时,只需要删除Map中的第一个元素即可。例如,this.cache.delete(this.cache.keys().next().value); 这行代码就能完成工作。

以上就是文章全部内容了,如果喜欢这篇文章的话,还希望三连支持一下,感谢!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小纯洁w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值