如何用函數表示數(二)引入閉包

在現實的編碼中,咱們更多的遇到的是像這樣的式子 java

g(f(x)+y,z)或f(x,y,z)之類 編程

所以如何表示多目成了一個問題。 閉包

你能夠說用f(x,y)表示x,y,在多目調用的地方則改寫成g(f(x,y))。 函數

且不說,就算能把g(x,f)改寫成g(x),那麼f(x,y)又用什麼方法來改寫呢?這就成了一個無頭無尾的問題。 工具

爲了消除在討論過程當中多元運算的影響,一個叫「閉包」的工具被引入了進來。閉包的做用,就是能將多目運算簡化爲一元運算。看個例子。 編碼

平時咱們寫加法運算是這樣寫的。 url


var i = 3+4;
咱們也能夠定義個函數來完成加法。



function add(a,b){return a+b;}

print(add(3,4));
這看上去有點傻。但它的結果是同樣的。


咱們還能夠用閉包,將加法這個二元運算改成1元運算 spa


function add2(a){
  return function(b){
    return a+b;
  }
}

print(add2(3)(4));
對閉包不熟悉的童鞋,這裏稍微作點講解。



print(add2(3)(4));


分解爲 code


f = add2(3);
print(f(4));

看add2的源碼,add2返回的並非一個數,而是一個匿名的函數。這個函數幹什麼呢?當傳入4時,就返回3+4,當傳入5時,就返回3+5。 xml

這樣咱們就把二元函數改成了一元函數。

只學過Java的童鞋在這裏必定會想到了,「嗯,我能夠構建一個加法器,完成的事情是同樣的。」

正是如此,在傳統的Java中能夠這麼寫

class adder = new Adder(3);
System.out.println(adder.add(4));
System.out.println(adder.add(5));
這道出了關於閉包的兩個事實:


1.在現下支持閉包特性的語言,基本上都是經過相似類的結構來實現閉包的。

2.閉包是事實上的匿名類,臨時類,而閉包在絕大多數時候都是當匿名類,臨時類在使用的。

而閉包和類的最大共同點,就是他們可以在其內部保持狀態值。

能夠這麼說,若是不須要保持中間狀態值,就無使用閉包或類的必要。只學過Java的童鞋確定不會認同這句話,他們會在你耳邊抗議說,類比閉包更有用,類有更fancier的特性,好比「繼承」,「多態」等等……我認可,有些時候這些特性都頗有用,但在一些時候,他們跟咱們要解決的問題沒有任何關係。

好比,在不少時候我要的是一個結果,我並不關心中間過程。我會這麼寫一個下載文件的方法

function loadJSONDoc(url,callback)
{
	var xmlhttp=new XMLHttpRequest();
	if(callback){
		xmlhttp.onreadystatechange=function(){
			if (xmlhttp.readyState==4 && xmlhttp.status==200){
					callback(JSON.parse(xmlhttp.responseText));
			}
		  }; 
	}
	xmlhttp.open("POST",'/'+url,true);
	xmlhttp.send();
}

而若是用典型的面向對象的方法:

1.你須要定義一個封裝下載器的對象。
2.再定義一個Event對象,來封裝你須要傳遞的數據信息。
3.再定義一個Ilistener接口
4.在下載器上添加一個event listener用於監聽下載是否完成。
5.當listener監聽到了下載完畢後,再將下載的內容封裝到步驟2自定義的Event中,再dispatch這個event出去
6.在調用的地方,你先要實現一個步驟3中listener的接口
7.new一個步驟1中封裝器的對象
8.再在這個對象上添加event listener,把實現listener接口的對象監聽進去(你使用的是標準的接口嗎?不是的話還得自定義一個IDispather!,好吧,你又在慶幸你以前已經繼承了某個遙遠的類,這類已經前瞻性的把這些接口都實現了,而當你還在爲你的OO實現而自鳴得意時,這時須要一個新的接口而你的父類又不被容許實現這個接口時,你馬上傻缺了)
9.當listener接口的方法被調用時,你終於能夠獲得想要的東西了…………

記得學Java時,我和個人同窗無一不被其超大的代碼量,超繁瑣的過程給震懾,以致於咱們忙活了半天,也不明白咱們究竟在幹什麼,但咱們沒有對此表示過懷疑,覺得編程就必須這樣繁瑣,由於老師告訴咱們,Java是世界上最好的語言,學會Java你就不須要學其餘語言了
……過去的血淚就別再提了……終於有一天,你忍不住了,寫了個靜態方法把東西都扔到裏面去了事。由於最終你發現,你須要的只是經過一個回傳函數,把數據取出來而已。

因此我相信,即便是最忠實的OO信徒也不會反對這個觀點:把問題抽象到更高的層次去解決,能反映出問題的本質,併爲解決問題提供更大的靈活性。

若是老是停留在較低的層面上,就容易耽擱於細節而忘了最初的目的。

閉包剛好就是對類和結構的更高層次的抽象。扯得有點遠了,下一章咱們再回到主題上來。

ps:據說新版的Java和C++11都要支持閉包了,通過20年以後,他們終於意識到了!

(待續)

相關文章
相關標籤/搜索