《Web 前端面試指南》一、JavaScript 閉包深刻淺出

閉包是什麼?

閉包是內部函數能夠訪問外部函數的變量。它能夠訪問三個做用域:首先能夠訪問本身的做用域(也就是定義在大括號內的變量),它也能訪問外部函數的變量,和它能訪問全局變量。javascript

內部函數不只能夠訪問外部函數的變量,也能訪問外部函數的參數(parameters)。但注意,它只能訪問外部函數的 parameters ,而不能訪問外部函數的 arguments 對象。java

舉例說明

JavaScript 閉包

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

Jquery 閉包

$(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
相關文章
相關標籤/搜索