讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議74:使用高階函數 >

建議74:使用高階函數

高階函數作為函數式編程眾多風格中的一項顯著特徵,經常被使用。實際上,高階函數即對函數的進一步抽像。高階函數至少滿足下列條件之一:

❑接受函數作為輸入。

❑輸出一個函數。

在函數式語言中,函數不但是一種特殊的對象,還是一種類型,因此函數本身是一個可以傳來傳去的值。也就是說,某個函數在剛開始執行的時候,總可以送入一個函數的參數。傳入的參數本身就是一個函數。當然,這個輸入的函數相當於某個函數的另外一個函數。當函數執行完畢之後,又可以返回另外一個新的函數,這個返回函數取決於return fn{...}。上述過程出現3個不同的函數,分別有不同的角色。要達到這樣的應用目的,需要把函數作為一個值來看待。

JavaScript不但是一門靈活的語言,而且是一門精巧的函數式語言。下面看一個函數作為參數的示例。


document.write([2,3,1,4].sort);//"1,2,3,4"


這是最簡單的數組排序語句。實際上Array.prototype.sort還能夠支持一個可選的參數「比較函數」,其形式如sort(fn)。fn是一個函數類型的值,說明這裡應用到高階函數。再如,下面這個對日期類型排序的sort。


//聲明3個對象,每個對象都有屬性id和date

var a=new Object;

var b=new Object;

var c=new Object;

a.id=1;

b.id=2;

c.id=3;

a.date=new Date(2012,3,12);

b.date=new Date(2012,1,15);

c.date=new Date(2012,2,10);

//存放在arr數組中

var arr=[a,b,c];

//開始調試,留意id的排列是按1、2、3這樣的順序的

arr.sort(

function(x,y){

return x.date-y.date;

}

);

//已經對arr排序了,發現元素順序發生變化,id也發生變化。排序是按照日期進行的


在數組排序的時候就會執行「function(x,y){return x.date-y.date;}」這個傳入的函數。當沒有傳入任何排序參數時,默認當x大於y時返回1,當x等於y時返回0,當x小於y時返回-1。

除了瞭解函數作為參數使用外,下面再看看函數返回值作為函數的情況。定義一個wrap函數,該函數的主要用途是產生一個包裹函數。


function wrap(tag){

var stag='<'+tag+'>';

var etag='</'+tag.replace(/s.*/,'')+'>';

return function(x){

return stag+x+etag;

}

}

var B=wrap('B');

document.write(B('粗體字'));

document.write('<br>');

document.write(wrap('B')('粗體字'));


「var B=wrap('B');」這一語句已經決定了這是一個「加粗體」的特別函數,執行該B函數就會產生<b>…內容…</b>的效果。若是wrap('p'),就會產生<p>…內容…</p>的效果,若是wrap('li'),就會產生<li>…內容…</li>…的效果,依此類推。wrap('B')返回到變量B的是一個函數。若不使用變量,wrap('B')也是合法的JavaScript語句,只要最後一個括號前面的是函數類型的值即可。為什麼stag+x+etag中的stag/etag沒有輸入也會在wrap內部定義?因為warp作用域中就有stag、etag兩個變量。如果從理論上描述這一特性,應該屬於閉包方面的內容。

實際上,map函數即為一種高階函數,在很多的函數式編程語言中均有此函數。map(array,func)的表達式已經表明,將func函數作用於array中的每一個元素,最終返回一個新的array。應該注意的是,map對array和func的實現是沒有任何預先的假設的,因此稱為「高階」函數。


function map(array,func){

var res=;

for(var i=0,len=array.length;i<len;i++){

res.push(func(array[i]));

}

return res;

}

var mapped=map([1,3,5,7,8],function(n){

return n=n+1;

});

print(mapped);//2,4,6,8,9

var mapped2=map(["one","two","three","four"],function(item){

return"("+item+")";

});

print(mapped2);(one),//(two),(three),(four),為數組中的每個字符串加上括號


mapped和mapped2均調用了map,但得到了截然不同的結果。因為map的參數本身已經進行了一次抽像,map函數做的是第二次抽像,所以高階的「階」可以理解為抽像的層次。