Javascript函数记忆及应用

本文介绍了一种函数优化技术——记忆化(memoization),通过缓存函数的计算结果避免重复计算,提高函数执行效率。文章详细解释了如何在JavaScript中实现记忆化,并探讨了其优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

记忆化(memoization)是一种构建函数的处理过程,让函数能够记住计算的结果。简而言之,就是把第一次计算的结果按照传参保存到某个地方,当以后以相同参数调用时不需重复执行,直接返回上次的结果,从而避免重复而繁琐的计算,提高了函数的执行性能。

那么,到底把这些值保存到哪里呢?根据Javascript语法,函数是第一类对象,我们可以自由地给函数定义一些属性,于是很自然地可以想到,我们可以给函数定义一个属性以保存这些缓存的键值,下面是一个示例:

function square (a) {
    square.mem || (square.mem = {});
    var mitem = square.mem[a];
    if (typeof mitem !== "undefined") {
        console.log('get result from cache');
        return mitem;
    } else {
        console.log('get result by calculation');
        return (square.mem[a] = a * a);
    }
}
square(33) // get result by calculation 1089
square(33) // get result from cache 1089
square(33) // get result from cache 1089

从打印结果可知,第二次和第三次执行square函数,直接是从square.mem中拿数据,而不是再进行一次运算,我们的目的达到了。

但是这样做会存在一些问题,如果我把square.mem干掉,则所有已被缓存的键要被重新计算一次:

square.mem = undefined;
square(33) // get result by calculation 1089
square(33) // get result from cache 1089

而且如果人为地对square.mem进行一些赋值操作,在随后的函数调用时会得到错误的结果:

square.mem[33] = 233;
square(33) // get result from cache 233

考虑到这些封装性问题,我们可以使用闭包来解决:

var getElement = (function (){
   var mem = {};
   return function (selector) {
       var mitem = mem[selector];
       if (mitem) {
           console.log('get element from cache');
           return mitem
       } else {
           console.log('get element by dom api');
           return (mem[selector] = document.querySelector(selector));
       }
   } 
})()

getElement作为一个IIFE的执行结果,函数体内调用了上层匿名函数的mem变量,形成一个闭包。mem存在于封闭的匿名函数作用域内,不能被外部拿到,从而提高了封装性。

进一步,把这个功能封装成一个可复用的函数,也是一个常用的技巧:

function cached (fn) {
  const cache = Object.create(null);
  return (function cachedFn (str) {
    const hit = cache[str];
    return hit || (cache[str] = fn(str));
  })
}

var getElement = cached(selector => document.querySelector(selector));

另外,我们也可以利用闭包的存储功能,实现单例模式:

class Hole{
    self_url = './hole';
    self_goes = 'dooes';
}

var getUniqueHole = (function () {
    var hole = null;
    return function () {
        if (hole) {
            return hole
        } else {
            return (hole = new Hole);
        }
    }
})()

var a = getUniqueHole() // Hole {self_url: "./hole", self_goes: "dooes"}
var b = getUniqueHole() // Hole {self_url: "./hole", self_goes: "dooes"}

console.log(a === b) // true

第一次执行getUniqueHole会把创建的单例对象hole放在闭包内,下次调用时拿到的其实就是它,所以对两次调用,得到的是相同的引用。

根据以上分析,函数记忆可以优化执行效率,带来运行速度上的提升,但因计算值需要被缓存,必定需要额外的内存空间。对于小而简单的函数,使用这种方法可能适得其反。另外,只有执行多次和执行一次结果相同的函数才可以使用记忆化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值