最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 什么是闭包?

    正文概述 掘金(酸菜鱼一号)   2021-08-13   471

    这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

    1. 什么是闭包

    我们首先来看一个闭包的例子:

    function foo() {
        var a = 2
        
        function bar() {
            console.log(a)
        }
        
        return bar
    }
    
    var baz = foo()
    
    baz()            // 输出: 2
    
    • foo 函数传递出了一个函数 bar,传递出来的 bar 被赋值给 baz 并调用,虽然这时 baz 是在 foo 作用域外执行的,但 baz 在调用的时候可以访问到前面的 bar 函数所在的 foo 的内部作用域。
    • 由于 bar 声明在 foo 函数内部,bar 拥有涵盖 foo 内部作用域的闭包,使得 foo 的内部作用域一直存活不被回收。一般来说,函数在执行完后其整个内部作用域都会被销毁,因为 JavaScript 的 GC(Garbage Collection)垃圾回收机制会自动回收不再使用的内存空间。但是闭包会阻止某些 GC,比如本例中 foo() 执行完,因为返回的 bar 函数依然持有其所在作用域的引用,所以其内部作用域不会被回收。
    • 注意: 如果不是必须使用闭包,那么尽量避免创建它,因为闭包在处理速度和内存消耗方面对性能具有负面影响。

    2. 利用闭包实现结果缓存(备忘模式)

    function add(a) {
        return a + 1;
    }
    
    • 多次运行 add() 时,每次得到的结果都是重新计算得到的,如果是开销很大的计算操作的话就比较消耗性能了,这里可以对已经计算过的输入做一个缓存。
    • 所以这里可以利用闭包的特点来实现一个简单的缓存,在函数内部用一个对象存储输入的参数,如果下次再输入相同的参数,那就比较一下对象的属性,如果有缓存,就直接把值从这个对象里面取出来。
    /* 备忘函数 */
    function memorize(fn) {
        var cache = {}
        return function() {
            var args = Array.prototype.slice.call(arguments)
            var key = JSON.stringify(args)
            return cache[key] || (cache[key] = fn.apply(fn, args))
        }
    }
    
    /* 复杂计算函数 */
    function add(a) {
        return a + 1
    }
    
    var adder = memorize(add)
    
    adder(1)            // 输出: 2    当前: cache: { '[1]': 2 }
    adder(1)            // 输出: 2    当前: cache: { '[1]': 2 }
    adder(2)            // 输出: 3    当前: cache: { '[1]': 2, '[2]': 3 }
    

    使用 ES6 的方式会更优雅一些:

    /* 备忘函数 */
    function memorize(fn) {
        const cache = {}
        return function(...args) {
            const key = JSON.stringify(args)
            return cache[key] || (cache[key] = fn.apply(fn, args))
        }
    }
    
    /* 复杂计算函数 */
    function add(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 的索引,将 add 函数运行的结果当做索引的值传递给 cache,这样 adder 运行的时候如果传递的参数之前传递过,那么就返回缓存好的计算结果,不用再计算了,如果传递的参数没计算过,则计算并缓存 fn.apply(fn, args),再返回计算的结果。
    • 当然这里的实现如果要实际应用的话,还需要继续改进一下,比如:
    • 缓存不可以永远扩张下去,这样太耗费内存资源,我们可以只缓存最新传入的 n 个;
    • 在浏览器中使用的时候,我们可以借助浏览器的持久化手段,来进行缓存的持久化,比如 cookielocalStorage 等;
    • 这里的复杂计算函数可以是过去的某个状态,比如对某个目标的操作,这样把过去的状态缓存起来,方便地进行状态回退。
    • 复杂计算函数也可以是一个返回时间比较慢的异步操作,这样如果把结果缓存起来,下次就可以直接从本地获取,而不是重新进行异步请求。

    注意: cache 不可以是 Map,因为 Map 的键是使用 === 比较的,因此当传入引用类型值作为键时,虽然它们看上去是相等的,但实际并不是,比如 [1]!==[1],所以还是被存为不同的键。

    //  X 错误示范
    function memorize(fn) {        
      const cache = new Map()
      return function(...args) {
        return cache.get(args) || cache.set(args, fn.apply(fn, args)).get(args)
      }
    }
    
    function add(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 }
    

    起源地下载网 » 什么是闭包?

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元