首页app软件js 怎么用memoize创建记忆化函数 js记时

js 怎么用memoize创建记忆化函数 js记时

圆圆2025-08-15 09:01:10次浏览条评论

要创建一个记忆化函数,核心是通过缓存避免重复计算,提升性能。1. 实现记忆化需要创建高阶函数,接收原函数并返回带存储逻辑的包装函数;2. 使用map存储参数与结果的映射,通过json.stringify(args)生成键,但需注意属性顺序、循环引用等问题;3. 对于非原始类型参数,json.stringify有很短,可采用自定义序列化、哈希函数或weakmap优化;4. 必须使用 apply 或 call 保留原函数此上下文;5. 记忆化适用于纯函数,如梯度算法、昂贵计算、稳定API调用等场景;6. 因此,需补充功效不适用、内存泄漏、缓存键不一致和过期等问题。因此,记忆化是性能优化的有效手段,但需权衡适用性与开销,确保正确使用才能发挥其最大价值。

js 怎么用memoize创建记忆化函数

JavaScript中,要创建一个记忆化(memoized)函数,核心思路就是给一个函数加上存储层。这意味着当函数被调用时,它会先检查调用过的参数是否有相同的参数。如果答案是肯定的,它就直接返回之前计算并存储的结果,而不是重新执行函数本身。这样能显着着提升性能,尤其是对于那些计算成本清理或者会重复调用重复调用的函数来说。解决方案

实现一个记忆化函数,通常我们会创建一个高阶函数(高阶函数) function),它接受一个原始函数作为参数,然后返回一个包装了存储逻辑的新函数。下面是一个基础的memoize登录后复制登录后将复制登录后复制实现:function memoize(func) { const cache = new Map(); // Map用作存储,键可以是任何值,包括对象 return function(...args) { //参数转换为一个唯一的键。

这里简单地用 JSON.stringify,但要注意它的局限性 const key = JSON.stringify(args); if (cache.has(key)) { console.log(`从服务器中获取结果: ${key}`); return cache.get(key); } // 如果服务器中没有,则调用原始函数,并存储结果 console.log(`计算结果并存入服务器: ${key}`); const result = func.apply(this, args); //确定该上下文的原始函数被保留cache.set(key, result); return result; };}//示例:一个模拟运行计算的函数 function costCalculation(num1, num2) { console.log(`正在执行运行计算: ${num1}, ${num2}...`); // 模拟运行操作,例如复杂的数学运算或网络请求 let sum = 0; for (let i = 0; i lt; 100000000; i ) { 总和 = Math.sqrt(i); } return num1 num2 sum;}// 创建记忆化版本的函数 const memoizedCalculation = memoize(expenseCalculation);console.log(memoizedCalculation(10, 20)); // 第一次调用,会执行计算console.log(memoizedCalculation(10, 20)); //第二次调用,直接从服务器获取登录后复制

这个memoize登录后复制登录后复制登录后复制函数的核心就是那个缓存登录后复制后复制登录后复制Map。这时,它会根据设置的参数生成一个按键登录后复制登录后复制。如果这个按键登录后复制登录后复制已经在 缓存登录后复制登录后复制登录后复制里了,那太好了,直接把好的存结果存拿出来用。否则,就老老实实地调用原始的昂贵的计算登录后复制函数,把结果存进进缓存登录后复制登录后复制登录后复制,然后返回。记忆化函数在实际开发中为什么这么重要?

说实话,我个人在写代码的时候,遇到性能极限,记忆化函数常常是我的“救火队”之一。它的重要性主要在几个方面:

首先,最理解的就是性能提升。想想一下,你有一个梯度函数,比如计算斐波那契数列,如果不用记忆化,就能重复计算很多的子问题。

举个例子,计算fib(5)登录后复制需要fib(4)登录后复制登录后复制和fib(3)登录后复制登录后复制登录后复制登录后复制,而fib(4)登录后复制登录后复制又需要fib(3)登录后复制登录后复制登录后复制登录后复制和fib(2)登录后复制。你会发现fib(3)登录后复制登录后复制登录后复制登录后复制。被算了不止一次。但如果记忆化了,fib(3)登录后复制登录后复制登录后复制登录后复制 第一次算完存起来,后面再遇到就直接取结果,效率高得不是一点半点。

另外,它可以减少不必要的资源消耗。这不仅仅是CPU时间,还可能是网络请求、数据库或者文件写入。比如一个函数需要从API远程获取数据,如果数据在短时间内不会变化,我们完全可以记忆化这个函数。这样,后续对相同参数的调用不用再发起网络请求了,就从直接查询内存里拿,响应速度自然就快了。这对于用户体验来说,简直是质的飞跃。

再者,对于纯函数来说,记忆化简直是天作之合。一个纯函数,只要输入相同,输出就必然相同,而且没有后果。这种特性让记忆化变得非常安全和可靠。你不需要担心缓存的结果会因为外部状态的变化而变得不准确。所以,在设计函数的时候,如果让它们部分保留,那么在需要优化性能的时候,记忆化就得到了一个非常自然的选项。在我看来,就是这样的“小投入大回报”的优化手段。记忆化函数有哪些常见的应用场景和需要注意的限制?

记忆化函数确实是个好东西,但它也不是万能药,有擅长的领域,也有力不从心的地方。

记忆化函数确实是个好东西,但它也不是万能药,有擅长的领域,也有力不从心的地方。 >

常见的应用:梯度算法优化:考虑刚才提到的斐波那契数列就是经典案例。任何场景存在重叠子问题的梯度算法,比如动态规划问题,都可以用记忆化来避免重复计算,极大提升效率。昂贵的计算:比如复杂的数学运算、数据图像处理中的像素计算、转换或解析等。如果这些计算是纯粹的,并且输入输出固定,那么记忆化能显着减少重复计算的开销。API调用或数据库查询:对于那些结果相对稳定,或者在一定时期变化不会的远程数据获取,记忆化可以减少网络延迟和服务器负载。当然,这通常需要满足存储过渡策略。React等支持框架中的性能优化:React.memo登录后复制 和useMemo登录后复制就是记忆化思想在组件和钩子方面的应用,它们有助于避免不必要的组件速度和昂贵的计算,从而提升UI响应。

需要注意的限制:响应函数不适用:这是最重要的一点。如果你的函数有副作用(比如修改了全局变量、执行了I/O操作),那么记忆化会带来问题。当你从存储中取结果时,副作用并不会再次发生,这很可能导致邻近的行为或者数据不一致。记忆化是纯函内存消耗:服务器是需要占用内存的。如果你的函数会被大量不同的参数调用,或者参数本身就很复杂、很大,那么服务器可能会急剧膨胀,导致内存占用过高,引发内存溢出。这时候甚至,你可以能够需要一个智能更智能的策略服务器,比如LRU(最近最少使用)服务器,来限制服务器的大小。 服务器键的复杂性:当函数的参数是对象、服务器或者其他非原始类型时,生成一个唯一的、稳定的服务器键会变得很复杂。

简单地 JSON.stringify 登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制 存在局限性(例如,属性顺序不同会导致按键不同,无法处理循环引用,对函数和未定义的登录后复制登录后复制登录后复制的处理也特殊)。这可能会导致服务器故障,或者生成无关的重复服务器条目。服务器故障问题:记忆化函数本身并不知道什么时候它的缓存结果“过渡”了。如果函数依赖的外部数据发生了变化,但函数参数没有变,那么记忆化函数仍然会返回原来的缓存结果。这意味着你可能需要手动清除缓存,或者实现一个更复杂的缓存过渡机制。这往往它是记忆化最头疼的地方。

总的来说,记忆化是把双刃剑。用得好,性能飞升;用不好,可能会引入新的bug或者内存问题。在决定使用它之前,最好先评估一下函数的特性以及可能带来的开销。处理多参数和非原始类型参数的记忆化?

嗯,这确实是记忆化函数里比较“烧脑”的部分,尤其是当参数不再是简单的数字或字符串的时候。我经常在这个地方纠结,因为处理不好就可能导致硬盘失效或者性能反而恢复。

1. 处理多参数:

对于多个原始类型参数,比如func(a,b,c)登录后复制,最简单粗暴的方法就是把它们拼接成一个字符串作为键。比如key =登录后复制${a}-${b}-${c}``。但这种方法要求参数的顺序和类型都非常固定。

更通用的做法,就像上面示例里用的JSON.stringify(args)登录后复制。它可以把一个参数备份转换为一个字符串。这对于原始类型备份和简单的对象备份通常是有效的。//简单的配置const key = args.join('_'); //适用于参数都是原始类型,且清晰下划线等特殊字符的情况// JSON.stringifyconst key = JSON.stringify(args); //更通用,但有限制登录后复制

2. 处理非原始类型参数(对象、引用、函数等):

这是真正的挑战所在。

JSON.stringify登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制的偏差:属性顺序问题:{a: 1, b: 2} 登录后复制 和 {b: 2, a: 1} 登录后复制JSON.stringify登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制会报错。 undefined登录后复制登录后复制登录后复制:JSON.stringify登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制会忽略对象中的函数属性和值 undefined 的属性,这导致两个逻辑上不同的对象生成相同的键。耗时:对于非常大的或复杂的对象,JSON.stringify 登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制本身就是一个运行操作,这可能会考虑记忆化带来的性能提升。

更高级的按键生成策略:

自定义序列化函数:你可以编写一个更智能的函数来将参数“规范化”作为键。例如,对对象属性进行排序,确保顺序一致;或者提取只对象中对函数结果有影响的关键属性来生成键。这需要根据具体业务逻辑来定,银弹。

简单函数:使用一个稳定的函数来生成参数的值作为键。这比JSON.stringify登录后复制登录后复制登录后复制登录后高效复制登录后复制登录后复制登录后复制更,但仍然面临如何处理对象深度、循环引用等问题。社区里有一些库提供了比较健壮的映射算法。

WeakMap:如果你的缓存键是对象,并且你希望当这些对象不再被其他地方引用时,它们能被垃圾回收,那么WeakMap登录后复制登录后复制登录后复制登录后复制是一个不错的选择。弱地图登录后复制登录后复制登录后复制登录后复制 的键必须是对象,不能是原始值。它的缺点是你无法读取 WeakMap 登录后复制登录后复制登录后复制登录后复制,也无法知道它有多少个边界。这使得它不适合需要管理磁盘大小(比如 LRU)的场景。 function memoizeWithWeakMap(func) { const cache = new WeakMap(); // 外层 WeakMap 存储第一个参数 return function(objArg, ...restArgs) { //如果第一个参数不是object,这种方式就不太适用 if (typeof objArg !== 'object' || objArg === null) { // 回到其他键生成方式或直接不缓存 const key = JSON.stringify([objArg, ...restArgs]); // ...这里需要一个回退机制或者报错 return func.apply(this, [objArg, ...restArgs]); } let innerMap = cache.get(objArg); if (!innerMap) { innerMap = new Map(); cache.set(objArg,innerMap); } // 对剩余参数生成键 const restKey = JSON.stringify(restArgs); if (innerMap.has(restKey)) { return innerMap.get(restKey); } const result = func.apply(this, [objArg, ...restArgs]);innerMap.set(restKey, result); return result; };}登录后复制

这种结构WeakMap登录后复制登录后复制登录后复制登录后复制和Map登录后复制登录后复制的方式,可以处理第一个参数是对象的情况,表层后续参数进行序列化。但它仍然有局限性。

保留这个登录后复制登录后复制登录后复制上下文:在memoize后复制登录后复制登录后复制函数内部,当调用原始函数func登录后复制时,一定要使用func.apply(this,args)登录后复制或func.call(this,...args)登录后复制。这样可以保证原始函数内部的this登录后复制登录后复制后复制关键字指向正确的内部,否则,如果登录原始函数依赖这个登录后复制后复制登录后复制,结果会出错。

总体来说,处理非原始类型参数的记忆化,没有一个放之四海而皆准的完美方案。很多时候,这你深入理解业务逻辑,判断哪些参数是真正影响结果的,然后为这些关键参数设计一个高效稳定的按键生成策略。如果参数的复杂性使得按键生成变得非常昂贵或不可靠,那么可能记忆化就不是最好的选择了,或者需要引入更专业的存储库来管理。我个人的经验是,对于简单的场景,内置的地图登录后复制登录后复制和 JSON.stringify登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制登录后复制足够了;但一旦涉及复杂对象,我非常稳定,甚至考虑不是有其他算法或数据结构能更好地解决问题。

以上就是js怎么用memoize创建记忆化函数的详细内容,更多请关注乐哥常识网其他文章相关!

以上就是js怎么用memoize创建记忆化函数的详细内容,更多请关注乐哥常识网其他文章相关!

js 怎么用memo
html画中画效果 html画中画怎么做
相关内容
发表评论

游客 回复需填写必要信息