首页 > 其他分享 >闭包小知识:闭包实现数据缓存、变量隔离,以及柯里化的使用场景

闭包小知识:闭包实现数据缓存、变量隔离,以及柯里化的使用场景

时间:2022-11-07 22:04:20浏览次数:136  
标签:闭包 function 缓存 const cache console 柯里化 return

缓存

什么是缓存函数?

接收一个函数,用闭包将每次函数执行的结果缓存起来

缓存例子1:

	/*
    * 闭包实现缓存
    * 属性:有个键--值   --->所以可以将缓存数据存放在一个对象中
    * 方法:缓存存储   setCache
    *      缓存的获取  getCache
    * */
    function  configCache(){
        var  obj={};//设置一个内部的对象 用来存储缓存数据;这个属性是私有的
        //对外暴露两个公共的方法
        return {
            setCache:function(k,v){//设置缓存
                obj[k]=v;
            },
            getCache:function(k){//获取缓存
                return obj[k];
            }
        };
    }
    var conf = configCache();
    console.log(conf);
    conf.setCache(1,'sdwqecwqv');
    console.log(conf.getCache(1));//sdwqecwqv
    /*
    * 注意下面这种情况,两次configCache()会产生不同的执行环境
    * */
    configCache().setCache(1,'sdwqecwqv');
    console.log(configCache().getCache(1));//undefined
    /*
    * 使用立即执行函数
    * */
    var cacheConfig = (function(){
        var obj={};
        return {
            setCache:function(k,v){
                obj[k]=v;
            },
            getCache:function(k){
                return obj[k];
            }
        }
    })();

上面代码能看到外部cache可以读取也可以设置useCache内部的data,由此实现了缓存。

缓存函数的应用场景

缓存函数-柯里化

const memoize = (fn) => {
    const cache = {};
    const func = (...args) => {
      const key = JSON.stringify(args); // 函数入参序列化成string做为本次递归结果的唯一标识
      return cache[key] || (cache[key] = fn.apply(null, args)); // 如果缓存中有则直接返回 否则执行本次递归并将结果和key存入缓存
    };
    return func;
};

例子1:求1到n的和优化

//使用缓存函数
const add = (n) => {
  console.log('执行了一次');
  if (n === 1) {
    return 1;
  }
  return add(n - 1) + n;
};


const addFn = memoize(add);
console.log(addFn(3)); // 执行3次
console.log(addFn(3)); // 不会执行 缓存里有结果了 直接取

------------------------------------------------------------------------------------
//不使用
const useAdd = () => {
  const cache = {};
  const add = (n) => {
    console.log('执行了', cache);
    if (n === 1) {
      return 1;
    }
    return cache[n] || (cache[n] = add(n - 1) + n); // 如果缓存有值取缓存,否则继续递归存入缓存
  };
  return add;
};
const add = useAdd();
console.log(add(3)); // 会调用3次
console.log(add(2)); // 2已经缓存过了 所以只调用一次 从缓存中取

例子2:求斐波那契数列优化

const useGetRabbit = () => {
    const cache = {};
    return function getRabbit(n) {
      if (n === 1 || n === 2) {
        return 1; // 出生后第三个月开始每个月才会生一对兔子,第三个月之前还是1对
      }
      return cache[n] || (cache[n] = getRabbit(n - 1) + getRabbit(n - 2)); // 如果缓存有就取缓存 否则当前月兔子对数取前两个月之和
    };
};
const getRabbit = useGetRabbit();
console.log(getRabbit(48)); // 如果不用闭包缓存48个月就算不出来报栈溢出错误了

利用闭包及纯函数的特性来缓存函数每次调用的结果提高性能,避免重复的计算,使用时要注意一定要是个纯函数!

隔离变量

function useCache () {
      const data = {};
      const setItem = (key, value) => {
          data[key] = value
      }
      const getItem = key => {
          return data[key]
      }
      return {
          state: {
              data
          },
          getItem,
          setItem
      }
  }
  const cache = useCache()
  console.log(cache.state.data);
  cache.setItem('a', '1')
  cache.getItem('a')

可以这样看,data、setItem、getItem是被隔离在useCache内的数据或方法,在外部可以读取到或调用,但也只有cache有这个权限。

函数柯里化

通俗的说,柯里化是指一个函数,他接收函数作为参数,运行后能够返回一个新的函数。并且这个新的函数能够处理函数A的剩余参数,完整柯里化通常依赖 JS 的闭包。

function curry(fn) {
  let arr = [];
  return function curried(...args) {
    arr = [...arr, ...args]
    if (arr.length > fn.length) {
      return fn(...arr);
    }
    return curried;
  }
}

function sum(a, b, c) {
  return a * b * c;
}

let sum2 = curry(sum)
let sum3 = curry(sum)
console.log(sum2(1)(2)(2, 3)) // 4
console.log(sum3(1)(2)(3, 2)) // 6

上面的例子中,被传入的函数 fn 的调用时机,依赖于它自身的参数个数,当闭包属性 arr 长度达到要求后,才会被调用。这样的需求我个人感觉不多,那么函数柯里化的好处是什么?下面是柯里化更常用的一种场景:

// 更自由的组合参数
function createRequest(option, request) {
  return function (args) {
    return request({...option, ...args })
  }
}

function request(option) {
  return option;
}

let longRequest = createRequest({
  timeout: 24 * 3600 * 1000
}, request)

let longAndUserRequest = createRequest({
  api: '/user'
}, longRequest)

var res = longAndUserRequest({ name : '111' })
// api: "/user"
// name: "111"
// timeout: 86400000

它可以自由组合参数,让代码更简洁,比如 Function.prototype.bind 以及 axios.create 都使用了 函数柯里化的特性,我认为函数柯里化是一种设计模式,有点类似组合模式。这种组合方式缺点是:导致调用栈增加。

标签:闭包,function,缓存,const,cache,console,柯里化,return
From: https://www.cnblogs.com/guibi/p/16867620.html

相关文章

  • 从根上理解Mybatis的一级、二级缓存
    1\.写在前头这篇帖子主要讲一级缓存,它的作用范围和源码分析(本来想把一二级缓存合在一起,发现太长了)2\.准备工作2.1两个要用的实体类publicclassDepartment{public......
  • 数据缓存
    文档位置API-数据缓存1.本地缓存将数据存储在本地缓存中指定的key中。会覆盖掉原来该key对应的内容。除非用户主动删除或因存储空间原因被系统清理,否则数据都一......
  • 细说JavaScript闭包
    JavaScript闭包难点剖析一、作用域基本介绍ES6之前只有全局作用域与函数作用域两种,ES6出现之后,新增了块级作用域1.全局作用域在JavaScript中,全局变量是挂载在windo......
  • ASP.NET Core教程-Configuration(配置)- Cache(缓存)
    更新记录转载请注明出处:2022年11月7日发布。2022年11月5日从笔记迁移到博客。缓存缓存的概念缓存(Caching)是系统优化中简单又有效的工具,投入小收效大。数据库中......
  • 手写本地缓存实战1——各个击破,按需应对实际使用场景
    大家好,又见面了。本文是笔者作为掘金技术社区签约作者的身份输出的缓存专栏系列内容,将会通过系列专题,讲清楚缓存的方方面面。如果感兴趣,欢迎关注以获取后续更新。通......
  • Vue3 用keep-alive实现tab页缓存
    一般国产味道的后台界面,都会有一个类似于浏览器tab标签的东西大概就是这样的,可以点击标签切换不同的路由,并且不丢失之前录入的数据。但是vue-router默认是不支持该操......
  • 自建docker hub 容器缓存加速器下 的nginx 配置
    文档说明:只记录关键地方;dockerhub加速器nginx配置要求加速器只允许GETHEAD请求方法只允许docker-library/official-images通过加速器控制允许通过加速器......
  • 【博学谷学习记录】超强总结,用心分享 。分布式缓存
    分布式缓存--基于Redis集群解决单机Redis存在的问题单机的Redis存在四大问题:1.数据丢失问题:实现Redis数据持久化2.并发能力问题:搭建主从集群,实现读写......
  • 缓存穿透、缓存并发、缓存失效之思路变迁
    我们在用缓存的时候,不管是Redis或者Memcached,基本上会通用遇到以下三个问题:缓存穿透缓存并发缓存失效一、缓存穿透 Paste_Image.png Paste_Image......
  • MLIR-Bufferization缓存
    MLIR-Bufferization缓存概述什么是一次性缓存?缓存的目标目的地通过风格使用一次性缓存缓存区释放内存布局扩展单次缓存调试缓存区副本了解SSA使用定义链分析......