3

浅聊缓存函数_安东尼漫长的技术岁月的技术博客_51CTO博客

 1 year ago
source link: https://blog.51cto.com/u_13961087/5359481
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

浅聊缓存函数

原创

掘金安东尼 2022-06-06 09:06:01 ©著作权

文章标签 缓存 封装 高阶函数 文章分类 JavaScript 前端开发 阅读数162

前文,我们已经聊过了:柯里化函数、偏函数,根据高阶函数的定义:

高阶函指使用其他函数作为参数、或者返回一个函数作为结果的函数。

柯里化函数、偏函数,都是妥妥的高阶函数!传入一个原函数,返回一个新函数。新函数继承了原函数的能力,又发展出不同的新能力!!

牛哇牛哇,初级前端开发,用函数封装过程,高级前端开发,用函数封装函数。

本篇再介绍一个新的高阶函数 —— 缓存函数

浅聊缓存函数_封装

什么是缓存函数?什么情况下需要用到缓存函数?

本篇将用以下这个场景来解释:

比方说,我们这里有一个非常耗时的计算函数:

const calculate = (num)=>{
const startTime = new Date()
for(let i=0;i<num;i++){} // 大数计算
const endTime = new Date()
return endTime - startTime
}
console.log(calculate(10_000_000_000))

// 10465
复制代码

遍历数字 100 亿,需耗时 10s +,打印出这个值;

问:有什么办法能够缓存下来这个值,等到下次再次调用 ​​calculate(10_000_000_000)​​ 的时候,不需要再次进行计算,直接就输出这个计算结果??

有人说:着还不简单吗,直接把结果付给一个变量,​​let result = calculate(10_000_000_000)​​ ,下次直接拿 ​​result​​的值不就行了吗?

从实现上讲,当然是可以的;不过,从设计模式的角度来讲,就不太ok了!

因为声明 ​​result​​ 变量作为一个全局变量要一直存在,不管未来你用或者不用,它都存在着,占用着内存;

设想下,如果有 N 个大数的计算,要写 N 个变量去存吗?即使有一些是后面不会再重复被调用了的。

let result = calculate(10_000_000_000)
let result1 = calculate(10_000_000_0000)
......
let resultN = calculate(10_000_000_0000...000)
复制代码

又有朋友说了:“我有好主意”!

我直接声明一个缓存对象,再改写 ​​calculate​​ 函数内部,计算的时候判断一下,算过了的,就从对象里面拿,没算过的,才会进行计算。

代码如下:

let cacheObj = {}

const calculate = (num)=>{
if(!cacheObj[num]){
const startTime = new Date()
for(let i=0;i<num;i++){}
const endTime = new Date()
cacheObj[num] = endTime - startTime
}
return cacheObj[num]
}
复制代码

本瓜不得不说:你这孩子打小就聪明!

但是,忘记开闭原则了吗?我们尽量要少修改原函数,而是尽量在其基础上去拓展它。

这次是 ​​calculate​​ 函数需要缓存,下次,可能是 ​​balculate​​ 或 ​​dalculate​​ ,难道还要去改这些函数的内部判断吗?

当然不!!缓存函数就是来解决这种困境的!!

代码如下:

/*
* 缓存函数 cashed
*/

function cached(fn){ // 传入需要缓存结果的函数

const cacheObj = Object.create(null); // 创建一个对象

return function cachedFn (str) { // 返回回调函数
if ( !cacheObj [str] ) { // 在对象里面查询,函数结果是否被计算过
let result = fn(str);
cacheObj [str] = result; // 没有则要执行原函数,并把计算结果缓存起来
}
return cacheObj [str] // 被缓存过,直接返回
}
}

// calculate 计算大数的函数(也可以叫原函数)
const calculate = (num)=>{
const startTime = new Date()
for(let i=0;i<num;i++){}
const endTime = new Date()
return endTime - startTime
}
复制代码
// 经过缓存函数 cashed 将原函数 calculate 封装,让原函数具有缓存的新功能
let cashedCalculate = cached(calculate)

cashedCalculate(10_000_000_000) // 10465
复制代码

计算结果,就已经被缓存到 ​​cashedCalculate​​ 里面了,我们再次调用:

cashedCalculate(10_000_000_000) // 10465
复制代码

会立即得到计算结果 !


哈哈,就是这样巧妙的思路,如果问缓存函数的究极奥义是啥?

本瓜会答:是闭包!闭包太强了,用 cached 函数处理 calculate 的时候,就留下了一个闭包对象 cacheObj ,一直被存储着。并且返回的是回调函数,一样去接收后续的参数。

这样,既避免了多次创建全局变量,也避免了多次修改原函数内部。

用函数封装函数,高级!!

OK,以上便是本篇分享。点赞关注评论,为好文助力👍

我是掘金安东尼 🤠 100 万阅读量人气前端技术博主 💥 INFP 写作人格坚持 1000 日更文 ✍ 陪你一起度过漫长岁月 🌏


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK