在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); 这行代码就能完成工作。
以上就是文章全部内容了,如果喜欢这篇文章的话,还希望三连支持一下,感谢!
标签:缓存,return,前端,cache,value,LRU,key,键值 From: https://blog.csdn.net/weixin_43891869/article/details/139402635