閉包,有人說它是一種設計理念,有人說全部的函數都是閉包。我不知道如何去定義它,我也不許備去定義它,定義它就是限制了對它的理解。segmentfault
咱們依賴光來看清世間萬物,光卻遮住了黑暗。如同你腳下的路,讓你看不清前行的方向。數組
在這裏寫一點我對閉包的理解。理解閉包的關鍵在於:外部函數調用以後其變量對象本應該被銷燬,但閉包的存在使咱們仍然能夠訪問外部函數的變量對象。閉包
function outer() { var a = 1; return function() { return a; }; } var b = outer(); console.log(b()); //1
(function(){ var now = new Date(); if (now.getMonth() == 0 && now.getDate() == 1) { alert('Happy new Year!'); } })();
這種方式經常使用來限制向全局做用域添加過多的變量和函數。app
(funcion() { var a = 1; setA = function(val){ a = val; }; getA = function(){ return a; }; })(); console.log(a); //報錯 console.log(getA()); //1 setA(2); console.log(getA()); //2
也能夠這樣寫:函數
function outer(){ var a = 1; return { setA: function(val) { a = val; }, getA: function() { return a; } }; } var closure = outer(); console.log(a); //報錯 console.log(closure.getA()); //1 closure.setA(2); console.log(closure.getA()); //2
第一個例子中,setA
,getA
都是全局變量,用於讀寫私有變量a
。第二個例子中將這兩個方法做爲一個對象返回並賦給一個全局變量closure
,這樣當outer
執行完畢後,這兩個方法(匿名函數)連同外部函數中的變量對象(a
)依然不會被銷燬。this
閉包用於建立具備私有變量的實例對象,參考經過閉包建立具備私有屬性的實例對象設計
function arrFunc() { var arr = []; for (var i=0; i<10; i++) { arr[i] = function() { return i; }; } return arr; }
arr
數組中包含了10個匿名函數,每一個匿名函數都能訪問外部函數的變量i
,那麼i
是多少呢?當arrFunc
執行完畢後,其做用域被銷燬,但它的變量對象仍保存在內存中,得以被匿名訪問,這時i
的值爲10。要想保存在循環過程當中每個i
的值,須要在匿名函數外部再套用一個匿名函數,在這個匿名函數中定義另外一個變量而且當即執行來保存i
的值。code
function arrFunc() { var arr = []; for (var i=0; i<10; i++) { arr[i] = function(num) { return function() { return num; }; }(i); } return arr; } console.log(arrFunc()[1]()); //1
這時最內部的匿名函數訪問的是num
的值,因此數組中10個匿名函數的返回值就是1-10。對象
this
var name = 'window'; var obj = { name: 'object', getName: function() { return function() { return this.name; }; } }; console.log(obj.getName()()); //window
obj.getName()()
其實是在全局做用域中調用了匿名函數,this
指向了window
。這裏要理解函數名與函數功能是分割開的,不要認爲函數在哪裏,其內部的this
就指向哪裏。window
纔是匿名函數功能執行的環境。要想使this指向外部函數的執行環境,能夠這樣改寫:內存
var name = 'window'; var obj = { name: 'object', getName: function() { var that = this; return function() { return that.name; }; } }; console.log(obj.getName()()); //object
arguments
與this
也有相同的問題。下面的狀況也要注意:
var name = 'window'; var obj = { name: 'object', getName: function() { return this.name; } }; obj.getName(); //object (obj.getName = obj.getName)(); //window 非嚴格模式下
obj.getName();
這時getName()
是在對象obj
的環境中執行的,因此this
指向obj
。(obj.getName = obj.getName)
賦值語句返回的是等號右邊的值,在全局做用域中返回,因此(obj.getName = obj.getName)();
的this
指向全局。要把函數名和函數功能分割開來。
閉包會引用包含函數的整個變量對象,若是閉包的做用域鏈中保存着一個HTML
元素,那麼就意味着該元素沒法被銷燬。因此咱們有必要在對這個元素操做完以後主動銷燬。
function assignHandler() { var element = document.getElementById('someElement'); var id = element.id; element.onclick = function() { alert(id); }; element = null; }
當函數內部的定時器引用了外部函數的變量對象時,該變量對象不會被銷燬。
(function() { var a = 0; setInterval(function(){ console.log(a++); }, 1000); })();
閉包引用外部函數變量對象中的值;
在外部函數的外部調用閉包。
轉載請註明出處:http://www.javashuo.com/article/p-dwwykxew-cv.html
文章不按期更新完善,若是能對你有一點點啓發,我將不勝榮幸。