「閉包」 一詞來源於如下二者的結合:要執行的代碼塊(因爲自由變量被包含在代碼塊中,這些自由變量以及它們引用的對象沒有被釋放)和爲自由變量提供綁定的計算環境(做用域)。在PHP、Scala、Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby、 Python、Go、Lua、objective c、swift 以及Java(Java8及以上)等語言中都能找到對閉包不一樣程度的支持。javascript
函數做用域外調用函數做用域內變量的函數稱爲閉包。也就是其中一個函數是閉包。
嚴格意義上來說,任意函數均可以稱做爲閉包。java
1 function B(){ 2 var i=0; 3 return function(){ 4 alert(i++); 5 } 6 } 7 8 var A=B(); 9 10 //注意B();的返回值A(); 11 12 //0A(); 13 14 //1A是B函數做用外訪問B函數做用內變量 i 的函數;
一、函數b嵌套在函數a內部;
二、函數a返回函數b。
這樣在執行完var c=a( )後,變量c其實是指向了函數b,再執行c( )後就會彈出一個窗口顯示i的值(第一次爲1)。這段代碼其實就建立了一個閉包,爲何?由於函數a外的變量c引用了函數a內的函數b,就是說:
當函數a的內部函數b被函數a外的一個變量引用的時候,就建立了一個閉包。swift
簡而言之,閉包的做用就是在a執行完並返回後,閉包使得Javascript的垃圾回收機制不會收回a所佔用的資源,由於a的內部函數b的執行須要依賴a中的變量。這是對閉包做用的很是直白的描述,不專業也不嚴謹,但大概意思就是這樣,理解閉包須要按部就班的過程。
在上面的例子中,因爲閉包的存在使得函數a返回後,a中的i始終存在,這樣每次執行c(),i都是自加1後alert出i的值。
那 麼咱們來想象另外一種狀況,若是a返回的不是函數b,狀況就徹底不一樣了。由於a執行完後,b沒有被返回給a的外界,只是被a所引用,而此時a也只會被b引 用,所以函數a和b互相引用但又不被外界打擾(被外界引用),函數a和b就會被回收。(關於Javascript的垃圾回收機制將在後面詳細介紹)閉包
B運行完畢後,等待javascript垃圾回收就如同B運行完,系統回收了籠子,小狗是否是能夠被釋放了。但此時系統發現A是一顆大樹,穿過籠子拴住了小狗,系統沒法順利回收籠子,小狗也被大樹拴住了,籠子不能被回收,小狗也不能被釋放。要想回收籠子,釋放小狗就必須得先把大樹毀掉,或者解除繩索關聯。函數
1 function fun(n,o) { 2 console.log(o) 3 return { 4 fun:function(m){ 5 return fun(m,n); 6 } 7 }; 8 } 9 var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,? 10 var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,? 11 var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,? 12 //問:三行a,b,c的輸出分別是什麼?
1.使用閉包代替全局變量
2.函數外或在其餘函數中訪問某一函數內部的參數
3.在函數執行以前爲要執行的函數提供具體參數
4.在函數執行以前爲函數提供只有在函數執行或引用時才能知道的具體參數
5.爲節點循環綁定click事件,在事件函數中使用當次循環的值或節點,而不是最後一次循環的值或節點
6.暫停執行
7.包裝相關功能測試
1 function f1(){ 2 var test=111; 3 tmp_test=function(){return test;} //tmp_test是全局變量,這裏對test的引用,產生閉包 4 } 5 function f2(){ 6 alert("測試一:"+tmp_test()); 7 var test1=tmp_test(); 8 alert("測試二:"+test1); 9 } 10 f1();//測試一:111 11 f2();//測試二:111 12 alert(tmp_test()); //111 13 tmp_test=null;
某些狀況下,是沒法爲要執行的函數提供參數,只能在函數執行以前,提早提供參數。
有哪些狀況是延遲執行?
如:
setTimeOut
setInterval
Ajax callbacks
event handler[el.onclick=func 、 el.attachEvent("onclick",func)]spa
1 //沒法傳參的狀況 2 var parm=222; 3 function f1(){alert(111)} 4 function f2(obj){alert(obj)} 5 setTimeout(f1,500);//正確,無參數 6 var test1=f2(parm);//執行一次f2函數 7 setTimeout(f2,500);//undefined,傳參失敗 8 setTimeout(f2(parm),500);//參數無效,傳參失敗 9 setTimeout(function(parm){alert(parm)},500);//undefined,傳參失敗 10 document.getElementById("hello").onclick=f1;//正確 11 document.getElementById("hello").attachEvent("onclick",f1);//正確 12 //正確作法,使用閉包 13 function f3(obj){return function(){alert(obj)}} 14 var test2=f3(parm);//返回f3的內部函數的引用 15 setTimeout(test2,500);//正確,222 16 document.getElementById("hello").onclick=test2;//正確,222 17 document.getElementById("hello").attachEvent("onclick",test2);//正確,222