從我16年開始接觸前端,知道閉包這個詞,已通過去兩年了。這兩年裏,閉包這個概念我在不少地方瞭解過,卻實在沒有真的理解,長此以往,變成了一塊心病。這不,趁着如今項目告一段落的時間,我又開始折騰它了,在網易雲課堂上找了些資源來看。哈哈,多是由於已經作了些項目的緣故,此次終於能理解它究竟是個什麼玩意兒了。javascript
閉包,在《javascripts高級程序設計》裏面是這樣介紹的:閉包是指有權訪問另外一個做用域中的變量的函數。額。。這句話我之前看過不少遍,但依然不是很懂,只知道它是跟做用域有關。如今我知道了,若是這句話換成:但凡是內部的函數被保存到了外部,一定生成閉包。這樣就容易理解多了不是。
咱們如下面的這個代碼塊爲例:前端
function a() { const num = 100; function b () { num++; console.log(num); } return b; } const demo = a(); demo(); demo();
咱們先執行上述代碼,看看結果是什麼:java
爲何是這樣呢?數組
a()執行的結果是返回b,因此demo指的是b,則demo()指的是b();也就是說原先在a裏面的b,在b的外部執行;
咱們已經知道了做用域和做用域鏈的概念,上面代碼的做用域是這樣的:緩存
a執行完,返回了b,此時的b只是聲明,但還沒調用,因此沒有造成本身的AO,但做用域鏈和 a doing 時是同樣的,因此雖然 a() 的做用域被銷燬了,可是相同的一份卻被b保存到了外面。這也就是內部函數被保存到了外面造成閉包的本質。這樣也不難理解爲何上述代碼打印出來的值是那樣的了:閉包
這樣造成的閉包雖然可使外部能夠訪問到內部的函數,可是致使了原有的做用域鏈不釋放,會形成內存泄漏。(內存泄漏的意思就是佔用內存,可用內存資源變少了)。因此若是不是特殊須要,應儘可能防止這種狀況發生。模塊化
而且,做用域鏈的配置機制引出了一個值得注意的反作用:即閉包只取得包含函數中任何變量最後一個值,好比下面這個例子:函數
function createFunctions() { var result = []; for(let i = 0; i< 10; i++) { result[i] = function() { console.log(i); } } return result; } var myArr = createFunctions(); for(var j = 0; j < 10; j++) { myArr[j](); }
這個函數會返回一個函數數組,表面上看,彷佛每一個函數都應該有本身的索引值,即會返回:0,1,2...9;但實際上,每一個函數都會返回10;這是由於在createFunctions()執行時,for循環跳出的條件是i=10;因此函數返回後,i的值是10 ;而每一個result的做用域鏈中都保存這createFunctions()的AO,因此他們引用的都是createFunctions()的i值,因此每一個函數內部i的值都是10;
這樣,咱們能夠建立一個當即執行函數強制讓閉包的行爲符合預期:spa
function createFunctions() { var result = []; for(var i = 0; i < 10; i++) { (function(j) { result[i] = function() { document.write(j + " "); } }(i)); } return result; } var myArr = createFunctions(); for(var j = 0; j < 10; j++) { myArr[j](); }
下面看看幾個用到閉包的典型例子:設計
如累加器:調用多少次,累加多少次,用閉包更加模塊化
function add() { var count = 0; function demo() { count++; console.log(count); } return demo; } var counter = add(); counter();//1 counter();//2 counter();//3
如eater: eat和push保存的都是eater的AO;,因此eat中food改變後。其實是eater變了,因此也會影響push;
function eater() { var food = ''; var obj = { eat: function() { console.log('eating' + food); food = ''; }, push: function(myFood) { food = myFood; } } return obj;// 至關於返回裏面的eat和push操做food; } var eater1 = eater(); eater1.push('banana'); eater1.eat();