回複內容:
JavaScript 閉包的本質源自兩點,詞法範圍和函數當作值傳遞。
詞法範圍,就是,按照代碼書寫時的樣子,內建函式可以訪問函數外面的變數。引擎通過資料結構和演算法表示一個函數,使得在代碼解釋執行時按照詞法範圍的規則,可以訪問外圍的變數,這些變數就登記在相應的資料結構中。
函數當作值傳遞,即所謂的first class對象。就是可以把函數當作一個值來賦值,當作參數傳給別的函數,也可以把函數當作一個值 return。一個函數被當作值返回時,也就相當於返回了一個通道,這個通道可以訪問這個函數詞法範圍中的變數,即函數所需要的資料結構儲存了下來,資料結構中的值在外層函數執行時建立,外層函數執行完畢時理因銷毀,但由於內建函式作為值返回出去,這些值得以儲存下來。而且無法直接存取,必須通過返回的函數。這也就是私人性。
本來執行過程和詞法範圍是封閉的,這種返回的函數就好比是一個蟲洞,開了掛。也就是 @秋月涼 答案中輪子哥那句話的意思。
顯然,閉包的形成很簡單,在執行過程完畢後,返回函數,或者將函數得以保留下來,即形成閉包。實際上在 JavaScript 代碼中閉包不要太常見。
函數作為第一等對象之後 JavaScript 靈活得不要不要的。閉包其實是一個很通用的概念,正如 @寸志 老師說的:「閉包是詞法範圍和函數作為First-class的共同體現」。 而很多大家耳熟能詳的語言裡都支援函數作為一類對象,比如JavaScript,Ruby,Python,C#,Scala,Java8.....,而這些語言裡無一例外的都提供了閉包的特性,因為閉包可以大大的增強函數的處理能力,函數可以作為一類對象的這一優點才能更好的發揮出來。
那什麼是閉包呢,一言以蔽之:一個持有外部環境變數的函數就是閉包。
理解閉包通常有著以下幾個關鍵點:
1.函數
2.自由變數
3.環境
舉個栗子:
let a = 1let b = function(){ console.log(a)}
最近特別注意的看了一些關於閉包的東西,比方說這個
閉包的概念、形式與應用
算是大概理解了這麼個東西,基本掌握了這貨的用法,但對閉包意義的理解還是有點模糊,直到看到了這個 @vczh
感覺大部分回答太複雜了,過於理論化,繞來繞去,沒抓住本質和精髓。其實閉包沒那麼複雜。
最簡潔的回答:
閉包(Closure)就是一個有狀態(不消失的私人資料)的函數(對象);或者,閉包就是一個有記憶的函數。
MDN 上的定義是這樣的:
Closures are
functions that refer to independent (free) variables. In other words, the function defined in the closure 'remembers' the environment in which it was created.
Closures - JavaScript
首先,很明確,閉包是一個函數,一種比較特殊的函數。什麼是函數?函數就是一個基本的程式運行邏輯單位(模組),通常有一組輸入,有一個輸出結果,內部還有一些進行運算的程式語句。所以,那些僅僅說閉包是範圍(scopes)或者其它什麼的,是錯誤的,至少不準確。
MDN 中還有一段更詳細的解釋:
A closure is
a special kind of object that combines two things: a function, and the environment in which that function was created. The environment consists of any local variables that were in-scope at the time that the closure was created.
下面是一個最簡單的 JavaScript 閉包的例子,inc 就是一個閉包(函數/函式),它有一個私人資料(狀態) count。
var inc = (function () { // 該函數體中的語句將被立即執行 var count = 0; // 局部變數初始化 return function () { // 返回一個內嵌的閉包函數引用 return ++count; // 當外部函數 return 後,這裡的 count 不再是外部函數的局部變數。 };}) ();inc(); // count: 1inc(); // count: 2
其他答案的解釋都已經很詳細了,我就不多寫自己的理解了,但貌似都只有文字描述或許不方便新人理解,我就show我滴渣渣code吧,寫四個例子啦,如果看明白了就大概懂了~
寫四個理解JS閉包的例子 · Issue #8 · AutumnsWind/Good-text-Share · GitHub
第一個:
function love(name) { var text = 'Hello ' + name; var me = function() { console.log(text); } return me; } var loveme = love('AutumnsWind'); loveme(); // 輸出 Hello AutumnsWind
首先,閉包是一個函數,而且是一個有權訪問另一個函數範圍中的變數的函數。
最常見的閉包,就是在一個函數內部再建立一個函數。比如:
function f(a) { return function() { return a; }}
一點淺見
閉包就是當函數可以記住並訪問所在的詞法範圍時,就產生了閉包,即使函數是在當前詞法範圍之外執行。
常見的例子
1.
調用函數裡面的函數(函數裡面的函數能夠保證外面的函數的範圍不會被銷毀,所以無論是在函數裡面還是在外面調用函數裡面的函數都可以訪問到外層函數的範圍,具體做法可以將裡面函數當做傳回值返回後通過兩次的括弧調用)
2.
回呼函數(回呼函數會保留當前外層的範圍,然後回調到另一個地方執行,執行的時候就是閉包)
3.
IIFE方式(嚴格算也不是閉包,就是(function(){})()這種格式)
閉包有什麼用
個人淺見,由於閉包能保留所在的詞法範圍,所以可以在外部使用這個範圍,通過閉包設定和擷取裡面範圍變數的值,,也能將外部的資料通過參數傳進去範圍裡面去執行,
有點類似類的概念(可能說的有些不對)
迴圈和閉包
由於JavaScript沒有塊級範圍,所以迴圈直接是迴圈外面的範圍,如果迴圈是寫在全域中,則迴圈裡面的閉包保留的就是全域範圍,全域範圍只有一個,所以以下代碼不能實現每隔一秒輸出一個增加的數字(會連續輸出5個6,變數i是全域變數,迴圈很快執行,i一下子變成6,所以後面輸出的都是6)
for (var i=1; i<=5; i++) { setTimeout( function timer() { console.log( i ); }, i*1000 );}
相繼式(Sequent)中的前件(Antesedent)。不會js,就說閉包這個概念。
可以這麼理解:
Closure這個closed的意思是這個環境是閉合的,裡面運算不會依賴或者涉及到這個環境以外的東西。
所以一個函數的閉包就是它和它可能需要的所有外部的東西的最小集合,比如加上依賴的各種自由變數。下面的圖片出自https://gist.github.com/paulirish/4158604
,這是在Chrome Developer Tools裡一個圖示javascript closure的例子,我覺得很有用,貼過來。
Closure是函數的可訪問一個域,把這個域和window並列起來比較,這個概念就一點也不神秘了。