我理解的閉包

我以爲不少人都錯誤理解了閉包,或者說根本就不理解,只是人云亦云。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是一個「閉包」:包含了函數自己和其做用域鏈上的變量的一個總體。

相關文章
相關標籤/搜索