JavaScript學習(3):函數式編程


  在這篇文章里,我們討論函數式編程。

  什么是函數式編程?根據百度百科的描述,“函數式編程是種編程典范,它將電腦運算視為函數的計算。函數編程語言最重要的基礎是 λ 演算(lambda calculus)。而且λ演算的函數可以接受函數當作輸入(參數)和輸出(返回值)。和指令式編程相比,函數式編程強調函數的計算比指令的執行重要。和過程化編程相比,函數式編程里,函數的計算可隨時調用。

  可以看出,在函數式編程中,函數被看做是“一等公民”。JavaScript可以通過巧妙地函數組合來構建抽象,通過內嵌函數的方式,在軟件開發的過程中,我們可以把更多的精力放在“函數要做什么”上,而不用太關心“函數如何做”的問題。

  高階函數

  可以操作其他函數的函數,被稱為高階函數。例如我們想對數組的每一個元素做某種操作,那么我們需要遍歷整理數組,當操作發生改變時,我們還要重復編寫遍歷代碼,利用高階函數,可以簡化這個過程。示例如下:

 1 function forEach(array,func){
 2     for(var i = 0; i < array.length; i++){
 3         func(array[i]);
 4     }
 5 }
 6 
 7 var a = ["a","b","c"];
 8 forEach(a,function(obj){print(obj);});
 9 
10 forEach(a,function(obj){print(obj + 1);});
11 
12 
13 //輸出結果
14 a
15 b
16 c
17 a1
18 b1
19 c1

  forEach函數包含兩個參數,第一個參數是一個數組,第二個參數是一個函數,在forEach函數體內,會遍歷數組的每一個元素,然后針對每一個元素調用func函數。

  在調用forEach函數時,我們針對第二個參數,使用了匿名函數,它接受一個參數,這個參數其實就是數組中的元素。

  從這個示例中,我們可以看到通過這種方式,可以明顯簡化對數組的操作。

  修改函數

  我們可以通過高階函數很方便的修改已有函數的功能,示例如下:

 1 function reverse(func){
 2     return function(value){
 3         return !func(value);
 4     }
 5 }
 6 
 7 print(isNaN(NaN));
 8 var isNotNaN = reverse(isNaN);
 9 print(isNotNaN(NaN));
10 
11 
12 //輸出結果
13 true
14 false

  reverse的作用是逆轉傳入函數的操作,在示例中,isNaN函數返回傳入參數是否是NaN。

  規約函數

  Reduce函數通過重復調用一個函數,將數組轉換為單一的值。規約函數結構如下:

1 function reduce(combine,base,array){
2     forEach(array, function(value){
3         base=combine(base,value);
4         });
5     return base;
6 }

  Reduce函數中參數的順序是一個傳統,我們可以將第一個參數作為匿名函數的方式傳入。

  下面是兩個使用Reduce函數的示例,分別計算數組元素的和以及數組中0的個數:

 1 function countZeros(count,value){
 2     return value == 0 ?(count+1) : count;
 3 }
 4 
 5 function add(sum,value){
 6     return value+sum;
 7 }
 8 
 9 var a=[1,2,3,4,0];
10 print(reduce(add,0,a));
11 print(reduce(countZeros,0,a));
12 
13 
14 //輸出結果
15 10
16 1

  映射函數

  Map函數會遍歷數組,針對數組的每個元素,調用指定的操作,然后將操作得出的值存儲到另外一個數組中,並返回新數組。

  Map函數的結構如下:

1 function map(func,array){
2     var result=[];
3     forEach(array, function(value){
4         result.push(func(value));
5     });
6     return result;
7 }

  我們可以如下調用map方法:

1 var a=[1,2,3,4,0];
2         
3 print(map(function(value){
4                     return value*value;
5                 }, a));
6 
7 //輸出結果
8 1,4,9,16,0

  這個示例將數組中的每個元素進行平方操作,然后輸出。

  其他一些函數技巧

  操作符函數

  在JavaScript學習(1):基礎中,我們使用內嵌函數實現了四則運算,接下來我們試着另外一種實現方式:

 1 var a=[1,2,3,4,0];                
 2 var ops={"+":function(x,y){return x+y;},
 3         "-":function(x,y){return x-y;},
 4         "*":function(x,y){return x*y;},
 5         "/":function(x,y){return x/y;},
 6         };
 7 
 8 function operation(op, array){
 9     if (op in ops){
10         return reduce(ops[op],0,array);
11     }
12     else{
13         throw new Error("invalid operation.");
14     }
15 }
16 print(operation("+", a));
17 print(operation("^", a));
18 
19 //輸出結果
20 10

  對象中,屬性的值不僅僅可以是集合屬性,也可以是函數。上述示例使用這種方式對四則運算進行了封裝。然后調用reduce方法去計算數組元素的和。

  分布應用

  如果我們需要一個函數,但其中一個操作符的參數已經給定了,那應該如何處理?例如我們想對數組中的每個元素都做加1操作,使用map的方式實現:

1 print(map(function(value){
2             return value + 1;
3         },a));

  在這里,1是放在匿名函數中。我們還可以這樣做,使用分布應用的方式在外函數和內嵌函數中分別保存部分參數。

  分布應用的結構如下:

 1 function partial(func){
 2     var knowArgs=arguments;
 3     return function(){
 4         var realArgs=[];
 5         for(var i = 1; i < knowArgs.length; i++){
 6             realArgs.push(knowArgs[i]);
 7         }
 8         for(var i = 0; i < arguments.length; i++){
 9             realArgs.push(arguments[i]);
10         }
11         return func.apply(null, realArgs);
12     }
13 }

  如果想要實現上面同樣的功能,代碼如下:

1 print(map(partial(ops["+"], 1), a));

  需要注意的是partial函數中在內嵌函數中如何將外函數和內嵌函數的參數進行整合,構成完整的參數列表。

  以a=[1,2,3,4,0]為例,map函數會遍歷數組中的每一個元素,當遍歷到2時,參數的變化過程:

  1. 外函數參數:1) ops["+"]; 2) 1。

  2. 內嵌函數參數: 2

  3. 完整參數:1,2

  4. func.apply(null, realArgs): ops["+"](1,2)

  函數組合

  函數組合的含義是在調用函數A的過程中,它使用函數B來計算返回結果。

  就像這樣:

1 function compose(f1,f2){
2     return function(){
3         return f1(f2.apply(null,realArgs));
4     }
5 }

  上面示例中的isNotNaN也是這種情況。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM