高階函數介紹 高階函數曾經是函數式編程的一個概念,感覺是很高深的術語。但開發簡潔優雅的函數可以使代碼更加簡單明瞭。過去幾年中腳本語言採用了這些個技術,揭開了函數式編程的最佳慣用法的神秘面紗。高階函數就是將函數作為參數或返回值的函數。將函數做為參數(通常稱為回調函數)是一種強大、富有表現力的慣用法,在 ...
高階函數介紹
高階函數曾經是函數式編程的一個概念,感覺是很高深的術語。但開發簡潔優雅的函數可以使代碼更加簡單明瞭。過去幾年中腳本語言採用了這些個技術,揭開了函數式編程的最佳慣用法的神秘面紗。
高階函數就是將函數作為參數或返回值的函數。
將函數做為參數(通常稱為回調函數)是一種強大、富有表現力的慣用法,在JS中也大量使用。
一個例子
function compareNumbers(x,y){ if(x<y){ return -1; } if(x>y){ return 1; } return 0; } [3,1,3,1,5,9].sort(compareNumbers);//[1,1,3,4,5,9]
在標準庫的sort方法需要調用者傳遞一個具有compare方法的對象,但只有一個方法是必須的,所以直接傳遞一個函數更為簡潔。
這裡說的調用者傳遞一個具有compare方法的對象,測試好像沒用,測試代碼如下:
[3,1,3,1,5,9].sort({compare:function(a,b){return b-a;}});//[3,1,3,1,5,9]
返回的結果並不正確,不知道這裡為何?
數組的sort方法
在高3中對數組的sort方法里是這樣說的,sort()方法可以接收一個比較函數作為參數,以便指定哪個值位於哪個值的前面。
比較函數接收兩個參數,如果第一個參數應該位於第二個參數之前則返回一個負數,如果兩個參數相等則返回0,如果第一個參數應該位於第二個參數之後則返回一個正數。
對於數值類型或者其valueOf()方法會返回數值類型的對象類型,可以使用更簡單的比較函數。
function compare(a,b){ return b-a; }
許多數組的常見操作包含值得我們熟悉掌握的親切的高階函數抽象。
示例:有一個簡單的轉換字元串數組的操作。
var names=['Fred','Wilma','Pebbles']; var upper=[]; for(var i=0,n=names.length;i<n;i++){ upper[i]=names[i].toUpperCase(); } upper;//['FRED','WILMA','PEBBLES']
數組的map方法
高3中對數組的map方法的描述是:對數組中的每一項運行給定函數,返回每次函數調用的結果組成的數組。
上面的代碼可以改寫為
var names=['Fred','Wilma','Pebbles']; var upper=names.map(function(name){ return name.toUpperCase(); }); upper;//['FRED','WILMA','PEBBLES']
手動編寫自己的高階函數
需要引入高階函數抽象的信號是出現重覆或相似的代碼。
示例:
假如我們發現程式的部分代碼段使用英文字母構造一個字元串。
var aIndex='a'.charCodeAt(0);//97 var alphabet=''; for(var i=0;i<26;i++){ alphabet+=String.fromCharCode(aIndex+i); } alphabet;//"abcdefghijklmnopqrstuvwxyz"
同時,有一段生成包含數字的字元串
var digits=''; for(var i=0;i<10;i++){ digits+=i; } digits;//"0123456789"
另外其他地方還存在創建隨機字元串的代碼
var random=''; var aIndex='a'.charCodeAt(0);//97 for(var i=0;i<8;i++){ random+=String.fromCharCode(Math.floor(Math.random()*26)+aIndex) } random;
以上三段代碼都創建了一個不同的字元串,但它們都有著共同的邏輯。每個迴圈通過連接每個獨立部分的計算結果來創建一個字元串。可以把共用的部分進行提取。代碼如下:
function buildString(n,callback){ var res=''; for(var i=0;i<n;i++){ res+=callback(i); } return res; }
上面三段代碼,結果可以使用這個工具來進行創建。
var aIndex='a'.charCodeAt(0);//97 var alphabet=buildString(26,function(i){ return String.fromCharCode(aIndex+i); }); var digits=buildString(10,function(i){ return i; }); var random=buildString(8,function(){ return String.fromCharCode(Math.floor(Math.random()*26)+aIndex); })
從這裡可以看出創建高階函數節約了很多代碼。
創建高階函數的好處
1、正確地獲取迴圈邊界條件,可以放置在高階函數的實現中。
2、可以一次性地修改所有邏輯上的錯誤,不必去搜索散佈在程式中的該編碼模式的所有實例。
3、可以方便優化操作,因為代碼抽象出來,可以只修改一處。
4、可以給高階函數抽象一個清晰的名稱,可以使代碼的功能更清晰,而不需要深入細節。
當發現自己在重覆地寫一些相同的模式時,可以藉助於高階函數使代碼更簡潔、更高效、更可讀。留意一些常見的模式並將它們移到高階的工具函數中是一個重要的開發習慣。
提示
- 高階函數是那些將函數作為參數或返回值的函數
- 熟悉掌握現有庫中的高階函數
- 學會發現可以被高階函數所取代的常見的編碼模式
附錄一:數組的高階函數方法
除以下提供的sort和map方法,還有以下幾種方法。(主要的描述都摘自高3,而且除sort外,其它方法ES5中才有)
註:reverse方法並不能接收函數,所以並不是高階函數方法,它只是簡單都數組項進行反轉,並不對項進行排列。
1、every()方法
對數組中的每一項運行給定的函數,如果該函數對每一項都返回true,則返回true。
示倒:
var numbers=[1,2,3,4,5,4,3,2,1]; var res1=numbers.every(function(item,index,arr){ return item>0; }); var res2=numbers.every(function(item,index,arr){ return item>2; }); var res3=numbers.every(function(item,index,arr){ return item>5; }); res1;//true res2;//false res3;//false
2、some()方法
對數組中的每一項運行給定的函數,如果函數對於任一項返回true,就會返回true。
var numbers=[1,2,3,4,5,4,3,2,1]; var res1=numbers.some(function(item,index,arr){ return item>0; }); var res2=numbers.some(function(item,index,arr){ return item>2; }); var res3=numbers.some(function(item,index,arr){ return item>5; }); res1;//true res2;//true res3;//false
3、filter()方法
對數組中的每一項運行給定函數,返回該函數會返回true的項組成的數組。
var numbers=[1,2,3,4,5,4,3,2,1]; var res1=numbers.filter(function(item,index,arr){ return item>0; }); var res2=numbers.filter(function(item,index,arr){ return item>2; }); var res3=numbers.filter(function(item,index,arr){ return item>5; }); res1;//[1,2,3,4,5,4,3,2,1] res2;//[3,4,5,4,3] res3;//[]
4、forEach()方法
對數組中的每一項運行給定的函數。這個方法沒有返回值。
var numbers=[1,2,3,4,5,4,3,2,1]; var res2=[]; var res1=numbers.forEach(function(item,index,arr){ res2[index]=item*2; }); res1;//undefined res2;//[2, 4, 6, 8, 10, 8, 6, 4, 2]
5、reduce()方法
迭代數組的所有項,然後構建一個最終的返回的值。從數組的第一項開始,逐個遍歷到最後。接收兩個參數:一個是每一項上調用的函數和(可選)作為歸併基礎的初始值。
var numbers=[1,2,3,4,5,4,3,2,1]; var res1=numbers.reduce(function(prev,cur,index,arr){ return prev+cur }); var res2=numbers.reduce(function(prev,cur,index,arr){ return prev+cur },100); res1;//25 res2;//125
6、reduceRight()方法
迭代數組的所有項,然後構建一個最終的返回的值。從數組的最後一項開始,逐個遍歷到第一項。接收兩個參數:一個是每一項上調用的函數和(可選)作為歸併基礎的初始值。
var numbers=[1,2,3,4,5,4,3,2,1]; var res1=numbers.reduceRight(function(prev,cur,index,arr){ return prev+cur }); var res2=numbers.reduceRight(function(prev,cur,index,arr){ return prev+cur },100); res1;//25 res2;//125
reduce()方法和reduceRight()方法,主要取決於要從哪頭開始遍曆數組。除些之外,它們完全相同。