JavaScript 閉包
JavaScript 變量屬于本地
或全局
作用域。
全局變量能夠通過閉包
實現局部(私有)。
全局變量
函數能夠訪問函數內部定義的所有變量,比如:
實例
function myFunction() { var a = 4; return a * a; }
但是函數也能訪問函數外部定義的變量,比如:
實例
var a = 4; function myFunction() { return a * a; }
在最后這個例子中,a
是全局變量。
在網頁中,全局變量屬于 window 對象。
全局變量能夠被頁面中(以及窗口中)的所有腳本使用和修改。
在第一個例子中,a
是局部變量。
局部變量只能用于其被定義的函數內部。對于其他函數和腳本代碼來說它是不可見的。
擁有相同名稱的全局變量和局部變量是不同的變量。修改一個,不會改變其他。
不通過關鍵詞 var
創建的變量總是全局的,即使它們在函數中創建。
變量的生命周期
全局變量活得和您的應用程序(窗口、網頁)一樣久。
局部變量活得不長。它們在函數調用時創建,在函數完成后被刪除。
一個計數器的困境
假設您想使用變量來計數,并且您希望此計數器可用于所有函數。
您可以使用全局變量和函數來遞增計數器:
實例
// 初始化計數器 var counter = 0; // 遞增計數器的函數 function add() { counter += 1; } // 調用三次 add() add(); add(); add(); // 此時計數器應該是 3
上述解決方案有一個問題:頁面上的任何代碼都可以更改計數器,而無需調用 add()。
對于 add() 函數,計數器應該是局部的,以防止其他代碼更改它:
實例
// 初始化計數器 var counter = 0; // 遞增計數器的函數 function add() { var counter = 0; counter += 1; } // 調用三次 add() add(); add(); add(); //此時計數器應該是 3。但它是 0。
它沒有用,因為我們顯示全局計數器而不是本地計數器。
通過讓函數返回它,我們可以刪除全局計數器并訪問本地計數器:
實例
// 遞增計數器的函數 function add() { var counter = 0; counter += 1; return counter; } // 調用三次 add() add(); add(); add(); //此時計數器應該是 3。但它是 1。
它沒有用,因為我們每次調用函數時都會重置本地計數器。
JavaScript 內部函數可以解決這個問題。
JavaScript 嵌套函數
所有函數都有權訪問全局作用域。
事實上,在 JavaScript 中,所有函數都有權訪問它們“上面”的作用域。
JavaScript 支持嵌套函數。嵌套函數可以訪問其上的作用域。
在本例中,內部函數 plus()
可以訪問父函數中的 counter
計數器變量:
實例
function add() { var counter = 0; function plus() {counter += 1;} plus(); return counter; }
這樣即可解決計數器困境,如果我們能夠從外面訪問 plus()
函數。
我們還需要找到只執行一次 counter = 0
的方法。
我們需要閉包(closure)。
JavaScript 閉包
記得自調用函數嗎?這種函數會做什么呢?
實例
var add = (function () { var counter = 0; return function () {return counter += 1;} })(); add(); add(); add(); // 計數器目前是 3
例子解釋
變量 add
的賦值是自調用函數的返回值。
這個自調用函數只運行一次。它設置計數器為零(0),并返回函數表達式。
這樣 add 成為了函數。最“精彩的”部分是它能夠訪問父作用域中的計數器。
這被稱為 JavaScript 閉包。它使函數擁有“私有”變量成為可能。
計數器被這個匿名函數的作用域保護,并且只能使用 add 函數來修改。
閉包指的是有權訪問父作用域的函數,即使在父函數關閉之后。