在初學JavaScript函數式編程的時候,常常會出現使人出乎意料的結果,而緣由,大都是因爲不理解JavaScript閉包引發的;理解JavaScript的閉包,能夠從JavaScript的閉包內部機制出發。html
函數:編程
function creatFunctions() { var result = []; for (var i = 0; i < 10; i++) { result[i] = function () { return i }; } return result; }; console.log(creatFunctions())
結果:數組
0: function creatFunctions/result[i]() 1: function creatFunctions/result[i]() 2: function creatFunctions/result[i]() 3: function creatFunctions/result[i]() 4: function creatFunctions/result[i]() 5: function creatFunctions/result[i]() 6: function creatFunctions/result[i]() 7: function creatFunctions/result[i]() 8: function creatFunctions/result[i]() 9: function creatFunctions/result[i]()
這個函數會返回一組數組,應該每一個函數都應該返回對應的值;而出現結果的緣由就在於閉包不是當即執行的。閉包
JavaScript高設中,閉包是指有權訪問另外一個函數做用域的中的變量的函數,建立閉包的形式,就是在函數的內部建立另外一個函數。函數式編程
function makeFunc() { var name = "Mozilla"; function displayName() { alert(name); } return displayName; } var myFunc = makeFunc(); myFunc();//MDN
displayName就是一個閉包。函數
閉包的內部機制在於理解做用域鏈,函數的執行環境,變量對象和活動對象;this
函數被調用時會建立一個執行環境,在執行環境中初始化該函數的活動對象、變量對象、和做用域鏈。其中,做用域鏈是指向當前執行環境可訪問的變量對象的指針,是一個由內而外的過程。以上述函數爲例;spa
因爲全部函數的執行環境底層都是在全局執行環境中,JavaScript在運行時,首先初始化活動對象,即函數自帶的arguments、this變量;全局變量makeFunc、myFunc會綁定到變量對象上;該執行環境初始化以後會推入棧中,此後會初始化函數makeFunc的局部變量,讀取局部變量'name';內部函數displayName的函數聲明;值得注意的是,每一個執行環境的內部屬性[[scope]] 保存當前執行環境可訪問的變量對象,由內而外,即內部函數始終能夠訪問外部函數直到全局環境的全部變量,而外部環境是沒法讀取內部函數變量對象的;這是因爲做用域鏈中定義的。指針
通常函數在執行完成後,會銷燬其執行環境,只保留全局執行環境中變量;可是在閉包中,因爲閉包並非當即執行,而是保存着內部函數執行須要的全部變量,包括內部變量和外部變量;在須要時再執行;函數執行完畢後;因爲內部匿名函數的做用於鏈依然引用這個活動對象;所以活動對象仍然會保存在內存中。code
閉包只是對函數內部的活動對象的保留,JavaScript中對變量只能保存一個定義;即 i 只能保存一個值,也就是上例中 i=10;閉包不是當即執行,當閉包執行時;向上搜索函數用到的全部變量,而內存中保存的是最後一次i的值;所以 i=10;
閉包會引用包含它函數的做用域,所以比普通函數佔用更多的內存。