这是我参与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个; - 在浏览器中使用的时候,我们可以借助浏览器的持久化手段,来进行缓存的持久化,比如 
cookie、localStorage等; - 这里的复杂计算函数可以是过去的某个状态,比如对某个目标的操作,这样把过去的状态缓存起来,方便地进行状态回退。
 - 复杂计算函数也可以是一个返回时间比较慢的异步操作,这样如果把结果缓存起来,下次就可以直接从本地获取,而不是重新进行异步请求。
 
注意: 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介绍。
 
- 提示下载完但解压或打开不了?
 
- 找不到素材资源介绍文章里的示例图片?
 
- 模板不会安装或需要功能定制以及二次开发?
 
                    
    
发表评论
还没有评论,快来抢沙发吧!