十万个Web前端面试题之闭包

439 阅读3分钟
闭包的知识点和作用域息息相关。大家如果想了解作用域,可以看我之前的一篇作用域的文章。了解作用域的,都知道,局部作用域可以获取全局作用域的变量,但反之是不行的。那怎样才能让全局作用域可以访问到局部作用域的变量呢?这就产生了闭包。

概念

当内部函数尝试访问其外部函数的作用域链,即在直接词法作用域之外的变量时,会创建一个闭包。 闭包包含自己的作用域链,父级的作用域链和全局作用域。闭包不仅可以访问其外部函数中定义的变量,还可以访问外部函数的参数。
这样说,真的很拗口。
我个人理解的话,可以简单这样记
  • 闭包内的变量或函数的内存占用在一个生命周期内永远不被销毁,意味着你的执行上下文栈被销毁了,它还在,所以你可以用来做计数器之类的功能,不过早期由于老旧浏览器的Bug,在闭包中操作DOM会导致内存溢出现象(现在一般不会,所以内存溢出现象基本也不考虑了),因为执行上下文栈被销毁,但函数的激活对象由于使用了闭包,是不被销毁的,所以内存占用会大点,如果有进行性能优化,可以关注下这块
  • 闭包就是为了解决跨作用域的调用问题

示例

我们还是看看常用的闭包例子吧
1、计数器
function counter() {
  let count = 0
  function counterPlus() {
    count++
    console.log(count)
  }
  return counterPlus
}
var cp = counter() // 通过cp可以直接反馈counter函数的count变量
cp() // 1
cp() // 2
cp() // 3
2、同一作用域变量共用
function counter() {
  var arr = []
  for (var i=1; i<=3; i++) {
    arr.push(function () {
      return i*i
    })
  }
}
var rst = count()
var f1 = rst[0]
var f2 = rst[1]
var f3 = rst[3]
f1() // 16
f2() // 16
f3() // 16
这边你其实想得到的是1,4,9,但是得到了三个16,因为在作用域那边我们讲过,JavaScript是静态作用域,它并非靠执行过程改变,当这边三个函数返回时,他们引用的变量i其实已经都是4了,所以结果是16,那我们怎么才能得到1,4,9呢?
简单,我们需要创建一个它自己的新作用域,就可以了,把arr.push这样改造
arr.push((function(n){
  return function () {
    return n*n
  }
})(i))
通过函数的参数绑定变量当前的值,无论变量后续如何变化,函数参数值是不变的
3、结果缓存
function getData() {
  let cache = {}
  return {
    add: function(id) {
      if (id in cache) {
        return cache[id]
      }
      var d = new ... // 获取数据
      cache[id] = d
      return d
    },
    clear: function(id) {
      if (id in cache) {
        cache[id].clearSelection()
      }
    }
  }
}
const searchData = getData()
searchData.add('jack')
这边通过数据,直接缓存,可以防止多次的网络请求,且缓存直接在函数内部,无需定义一个全局变量,操作很方便
4、私有化封装
const PersionClass = () => {
  let name = 'jack'
  return {
    getName: () => {
      return name
    },
    setName: (newName) => {
      name = newName
    }
  }
}
const persion = PersionClass()
console.log(persion.name) // undefined
console.log(person.getName()) // jack
persion.setName('lucy')
console.log(persion.getName()) // lucy
这边你会看到,这很像Java中的POJO,私有化变更,提供方法来设置,其实JavaScript的很多面向对象的操作方法的基石就是闭包,比如类和继承等。