JavaScript 閉包

JavaScript 變數可以是區域性變數或全域性變數。

私有變數可以用到閉包。


全域性變數

函式可以訪問由函式內部定義的變數,如:

例項

function myFunction() { var a = 4; return a * a; }

嘗試一下 ?

函式也可以訪問函式外部定義的變數,如:

例項

var a = 4; function myFunction() { return a * a; }

嘗試一下 ?

後面一個例項中, a 是一個 全域性 變數。

在web頁面中全域性變數屬於 window 物件。

全域性變數可應用於頁面上的所有指令碼。

在第一個例項中, a 是一個 區域性 變數。

區域性變數只能用於定義它函式內部。對於其他的函式或指令碼程式碼是不可用的。

全域性和區域性變數即便名稱相同,它們也是兩個不同的變數。修改其中一個,不會影響另一個的值。

Note 變數宣告時如果不使用 var 關鍵字,那麼它就是一個全域性變數,即便它在函式內定義。


變數生命週期

全域性變數的作用域是全域性性的,即在整個JavaScript程式中,全域性變數處處都在。

而在函式內部宣告的變數,只在函式內部起作用。這些變數是區域性變數,作用域是區域性性的;函式的引數也是區域性性的,只在函式內部起作用。


計數器困境

設想下如果你想統計一些數值,且該計數器在所有函式中都是可用的。

你可以使用全域性變數,函式設定計數器遞增:

例項

var counter = 0; function add() { return counter += 1; } add(); add(); add(); // 計數器現在為 3

嘗試一下 ?

計數器數值在執行 add() 函式時發生變化。

但問題來了,頁面上的任何指令碼都能改變計數器,即便沒有呼叫 add() 函式。

如果我在函式內宣告計數器,如果沒有呼叫函式將無法修改計數器的值:

例項

function add() { var counter = 0; return counter += 1; } add(); add(); add(); // 本意是想輸出 3, 但事與願違,輸出的都是 1 !

嘗試一下 ?

以上程式碼將無法正確輸出,每次我呼叫 add() 函式,計數器都會設定為 1。

JavaScript 內嵌函式可以解決該問題。


JavaScript 內嵌函式

所有函式都能訪問全域性變數。

實際上,在 JavaScript 中,所有函式都能訪問它們上一層的作用域。

JavaScript 支援巢狀函式。巢狀函式可以訪問上一層的函式變數。

該例項中,內嵌函式 plus() 可以訪問父函式的 counter 變數:

例項

function add() { var counter = 0; function plus() {counter += 1;} plus(); return counter; }

嘗試一下 ?

如果我們能在外部訪問 plus() 函式,這樣就能解決計數器的困境。

我們同樣需要確保 counter = 0 只執行一次。

我們需要閉包。


JavaScript 閉包

還記得函式自我呼叫嗎?該函式會做什麼?

例項

var add = (function () { var counter = 0; return function () {return counter += 1;} })(); add(); add(); add(); // 計數器為 3

嘗試一下 ?

例項解析

變數 add 指定了函式自我呼叫的返回字值。

自我呼叫函式只執行一次。設定計數器為 0。並返回函式表示式。

add變數可以作為一個函式使用。非常棒的部分是它可以訪問函式上一層作用域的計數器。

這個叫作 JavaScript 閉包。它使得函式擁有私有變數變成可能。

計數器受匿名函式的作用域保護,只能通過 add 方法修改。

Note

閉包是一種保護私有變數的機制,在函式執行時形成私有的作用域,保護裡面的私有變數不受外界干擾。

直觀的說就是形成一個不銷燬的棧環境。