JS 利用高阶函数实现函数缓存(备忘模式)

1. 高阶函数

高阶函数就是那种输入参数里面有一个或者多个函数,输出也是函数的函数,这个在js里面主要是利用闭包实现的,最简单的就是经常看到的在一个函数内部输出另一个函数,比如

var test = function() {
    return function() {}
}

这个主要是利用闭包来保持着作用域:

var add = function() {
    var num = 0;
    return function(a) {
        return num = num + a;
    }
}
add()(1);      // 1
add()(2);      // 2

这里的两个add()(1)和add()(2)不会互相影响,可以理解为每次运行add函数后返回的都是不同的匿名函数,就是每次add运行后return的function其实都是不同的,所以运行结果也是不会影响的。

如果换一种写法,比如:

var add = function() {
    var num = 0;
    return function(a) {
        return num = num + a;
    }
}
var adder = add();
adder(1); // 1
adder(2); // 3

这样的话就会在之前运算结果基础上继续运算,意思就是这两个 adder 运行的时候都是调用的同一个 num

2. 高阶函数实现缓存(备忘模式)

比如有个函数:

var add = function(a) {
    return a + 1;
}

每次运行add(1)的时候都会输出2,但是输入1每次还是会计算一下1+1,如果是开销很大的操作的话就比较消耗性能了,这里其实可以对这个计算进行一次缓存。 所以这里可以利用高阶函数的思想来实现一个简单的缓存,我可以在函数内部用一个对象存储输入的参数,如果下次再输入相同的参数,那就比较一下对象的属性,把值从这个对象里面取出来。

const memorize = function(fn) {
  const cache = {}
  return function(...args) {
    const _args = JSON.stringify(args)
    return cache[_args] || (cache[_args] = fn.apply(fn, args))
  }
}
const add = function(a) {
  return a + 1
}
const adder = memorize(add)
adder(1)    // 2    cache: { '[1]': 2 }
adder(1)    // 2    cache: { '[1]': 2 }
adder(2)    // 3    cache: { '[1]': 2, '[2]': 3 }

JSON.stringify把传给 adder 函数的参数变成了字符串,并且把它当做 cache 的 key,将 add 函数运行的结果当做 value 传到了 cache 里面,这样 memorize 的匿名函数运行的时候会返回cache[_args],如果cache[_args]不存在的话就返回fn.apply(fn,args),把fn.apply(fn, arguments)赋值给cache[_args]并返回。 注意:cache不可以是Map,因为Map的键是使用===比较的,[1]!==[1],因此即使传入相同的对象或者数组,那么还是被存为不同的键。

const memorize = function(fn) {        //  X 错误示范
  const cache = new Map()
  return function(...args) {
    return cache.get(args) || cache.set(args, fn.apply(fn, args)).get(args)
  }
}
const add = function(a) {
  return a + 1
}
const adder = memorize(add)
adder(1)    // 2    cache: { [ 1 ] => 2 }
adder(1)    // 2    cache: { [ 1 ] => 2, [ 1 ] => 2 }
adder(2)    // 3    cache: { [ 1 ] => 2, [ 1 ] => 2, [ 2 ] => 3 }

本文是系列文章,可以相互参考印证,共同进步~

  1. JS 抽象工厂模式
  2. JS 工厂模式
  3. JS 建造者模式
  4. JS 原型模式
  5. JS 单例模式
  6. JS 回调模式
  7. JS 外观模式
  8. JS 适配器模式
  9. JS 利用高阶函数实现函数缓存(备忘模式)
  10. JS 状态模式
  11. JS 桥接模式
  12. JS 观察者模式

网上的帖子大多深浅不一,甚至有些前后矛盾,在下的文章都是学习过程中的总结,如果发现错误,欢迎留言指出~

参考: <JavaScript模式>P78

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java面试笔试题

内存中的栈(stack)、堆(heap)和静态区(static area)的用法

通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用内存中的栈空间;而通过new关键字和构造器创建的对象放在堆空间;程序中的字面...

12760
来自专栏北京马哥教育

Python程序员最常犯的十个错误,看完你自己都笑了

本文由马哥教育Python自动化实战班4期学员推荐,转载自简书,作者为EarlGrey,内容略经小编改编和加工,观点跟作者无关,最后感谢作者的辛苦贡献与付出。 ...

29140
来自专栏web前端教室

一起升级技能,先行者课程学习笔记

-- Js运行之前的那么一瞬间,生成一个活动对象(Active Object),简直AO对象。

8940
来自专栏大数据

在Python中什么时候用Yield什么时候用Return

许多Python开发人员在代码中使用yield,而不考虑他们是否真的需要。这篇文章解释了你什么时候应该使用它。

40000
来自专栏程序员互动联盟

【答疑释惑】C++异常处理是咋回事?

疑惑一 C++的异常处理 一、什么是异常处理 一句话:异常处理就是处理程序中的错误。 二、为什么需要异常处理,以及异常处理的基本思想 C++ 之父Bjarne ...

33450
来自专栏java技术学习之道

一张图搞定Java原型模式

19050
来自专栏思考的代码世界

Python编程从入门到实践之字典|第5天

在Python中,字典是一系列键—值对。每个键都与一个值相关联,你可以使用键来访问与之 相关联的值。与键相关联的值可以是数字、字符串、列表乃至字典。事实上,可将...

37190
来自专栏企鹅号快讯

【C语言编程锦囊·连载42】scanf函数的返回值是什么?

问题阐述 scanf函数是用于数据输入的,输入变量的值被改变,那么scanf函数本身是否有返回值,返回值是什么意义呢? 专家解答 scanf函数的返回值很少有人...

23280
来自专栏思考的代码世界

Python编程从入门到实践之函数2|第9天

向函数传递列表很有用,这种列表包含的可能是名字、数字或更复杂的对象(如字典)。将列表传递给函数后,函数就能直接访问其内容。

37670
来自专栏Java爬坑系列

【Java入门提高篇】Day11 Java代理——JDK动态代理

  今天来看看Java的另一种代理方式——JDK动态代理   我们之前所介绍的代理方式叫静态代理,也就是静态的生成代理对象,而动态代理则是在运行时创建代理对象。...

21870

扫码关注云+社区

领取腾讯云代金券