我理解的闭包

我以为不少人都错误理解了闭包,或者说根本就不理解,只是人云亦云。ios

维基百科对于闭包的描述:axios

a closure is a record storing a function together with an environment: a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope)api

MDN对于闭包的描述:闭包

闭包是函数和声明该函数的词法环境的组合。app

“声明该函数的词法环境”即为函数的做用域链,所以能够说,一切函数都是闭包
而通常狭义的理解是,闭包就是引用了外部变量的函数和这些变量函数

const count = 0

function getCount () {
  console.log(count)
}

function run () {
  const count  = 1
  getCount()
}

run()

执行run的时候会发生什么?答案是打印'0',由于run里的getCount函数是一个闭包,包含了其声明时的外部变量count,所以count的值是外层的'0'。url

而大多数人,彷佛把“利用闭包实现私有变量”当成了闭包自己。prototype

不少讲闭包的文章都会写这个计数器函数的例子:code

const increase = function ()  {
  let count = 0
  return function () {
     return ++count
  }
}()
increase() // 1
increase() // 2

他们通常会说“这就是闭包”,却说不清楚到底什么是闭包。对象

函数内部的匿名函数,和其定义时的外部变量count,做为一个总体,就是闭包。

接下来将这个闭包return并赋值给外层的变量increase, 使该闭包随着increase存在于内存中而不是被GC销毁。count可以完成计数的功能,说明其也存在于内存中,证实了变量也是闭包的一部分。

大多数js库都会用一个自执行的匿名函数封装起来,正是利用了闭包的特性。以jQuery为例:

(function (global, factory) {
  ...
})(window, function (window) {
  var   version = "3.2.1"
  var support = {}
  var siblings = function () { ... }
  var jQuery = function () { ... }
  jQuery.prototype = { ... }
  ...
  window.jQuery = window.$ = jQuery
})

factory里定义的变量,在其余函数里被引用到,这些函数又成为了jQuery的原型方法,最后jQuery函数和内部的变量做为一个闭包,被“绑定”到window对象上。因而外层环境能够经过window.jQuery使用内部的变量和方法,却不能直接访问和修改。这就实现了私有变量和私有方法,同时还避免了污染全局环境。

闭包的主要用途,正是用来封装私有变量和方法,避免污染全局环境。
相似常见的用法还有函数debounce,throttle等。

实际上除了这些常被提起用法,咱们日常也常常用到闭包,举一个实际的例子:

// api.js
const axios = import('axios')
const url = '/getUserInfo'

export function getUserInfo() {
  return axios.get(url)
} 

// index.js
const { getUserInfo } = import('./api')

getUserInfo().then() ...

函数getUserInfo访问了其外部的变量axios和url,而在index中并未定义这二者,为何能够直接调用? 这正是由于被导入的getUserInfo是一个“闭包”:包含了函数自己和其做用域链上的变量的一个总体。

相关文章
相关标签/搜索