我理解的闭包

我觉得很多人都错误理解了闭包,或者说根本就不理解,只是人云亦云。

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

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)

MDN对于闭包的描述:

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

“声明该函数的词法环境”即为函数的作用域链,因此可以说,一切函数都是闭包
而一般狭义的理解是,闭包就是引用了外部变量的函数和这些变量

const count = 0

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

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

run()

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

而大多数人,似乎把“利用闭包实现私有变量”当成了闭包本身。

很多讲闭包的文章都会写这个计数器函数的例子:

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是一个“闭包”:包含了函数本身和其作用域链上的变量的一个整体。

posted @ 2017-08-29 15:12  huanglei-  阅读(1382)  评论(4编辑  收藏  举报