我以为不少人都错误理解了闭包,或者说根本就不理解,只是人云亦云。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是一个“闭包”:包含了函数自己和其做用域链上的变量的一个总体。