閉包是內部函數能夠訪問外部函數的變量。它能夠訪問三個做用域:首先能夠訪問本身的做用域(也就是定義在大括號內的變量),它也能訪問外部函數的變量,和它能訪問全局變量。javascript
內部函數不只能夠訪問外部函數的變量,也能訪問外部函數的參數(parameters)。但注意,它只能訪問外部函數的 parameters ,而不能訪問外部函數的 arguments 對象。java
function showName (firstName, lastName) { var nameIntro = "Your name is "; // 內部函數能夠訪問外部函數的變量(nameInfo)、parameter (firstName、lastName) function makeFullName () { return nameIntro + firstName + " " + lastName; } return makeFullName (); } showName ("Michael", "Jackson"); // Your name is Michael Jackson
$(function() { var selections = []; // 能訪問 selections 變量 $(".niners").click(function() { // 能更新變量 selections selections.push (this.prop("name")); }); });
JavaScript 的執行時候的做用域和建立時候的做用域是同樣的。這也就是說即便被外部函數返回後,內部函數仍然能訪問外部函數的變量。jquery
function celebrityName (firstName) { var nameIntro = "This celebrity is "; function lastName (theLastName) { return nameIntro + firstName + " " + theLastName; } return lastName; } var mjName = celebrityName ("Michael");// 這個時候外部方法 celebrityName 已經被返回了 // 閉包仍然能夠訪問外部方法的變量和參數 mjName ("Jackson"); // This celebrity is Michael Jackson
存儲的不是實際的值,在閉包被調用以前,若是外部函數中變量的值發生改變,會變得更有意思。數組
function celebrityID () { var celebrityID = 999; // 返回的包含內部函數的對象 return { getID: function () { // 內部函數返回的是更新之後的 celebrityID 變量值 return celebrityID; }, setID: function (theNewID) { // 內部函數隨時都能改變外部函數內的變量。 celebrityID = theNewID; } } } var mjID = celebrityID (); // 此時,外部函數的 celebrityID 變量被改變。 mjID.getID(); // 999 mjID.setID(567); // 改變外部函數的 celebrityID 變量。 mjID.getID(); // 567
開發中有以下狀況閉包
function celebrityIDCreator (theCelebrities) { var i; var uniqueID = 100; for (i = 0; i < theCelebrities.length; i++) { theCelebrities[i]["id"] = function () { return uniqueID + i; } } return theCelebrities; } var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0} ]; var createIdForActionCelebs = celebrityIDCreator (actionCelebs); var stalloneID = createIdForActionCelebs [0]; console.log(stalloneID.id()); // 103
在調用匿名函數的時候,uniqueID 已經加了 數字 3 變成 103,生成的 celebritiesID 也是 103,數組的每一個元素也就是都是 103,而不是 100、10一、102。函數
這是由於閉包(也便是例子中的內部匿名函數)訪問的是外部函數的變量的引用,而不是變量的值。爲了解決這個 BUG,咱們可使用一種 ** Immediately Invoked Function Expression ** (IIFE)(當即執行函數語法),代碼以下:ui
function celebrityIDCreator (theCelebrities) { var i; var uniqueID = 100; for (i = 0; i < theCelebrities.length; i++) { theCelebrities[i]["id"] = function (j) { // 這裏的 j 參數也就是在 調用(IIFE)時傳過來的參數 i。 return function () { return uniqueID + j; // 依次接收傳遞過來 i 值,而後把它保存在數組中。 } () // 經過在 function 末尾處加 () ,能夠當即執行它,而後只返回 uniqueID + j 的值,而不是 一個 function。 } (i); // 傳遞過來一個 i 變量做爲匿名函數的參數,並當即執行它。 } return theCelebrities; } var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}]; var createIdForActionCelebs = celebrityIDCreator (actionCelebs); var stalloneID = createIdForActionCelebs [0]; console.log(stalloneID.id); // 100 var cruiseID = createIdForActionCelebs [1]; console.log(cruiseID.id); // 101