天天看點

函數式程式設計

和Lisp、Haskell不同,javascript并非函數式程式設計語言,但在javascript中可以操控對象一樣操控函數,也就是說可以在javascript中應用函數式程式設計技術。ES5中的數組方法(如map()和reduce())就可以非常适合用于函數式程式設計風格。本文将詳細介紹函數式程式設計

  假設有一個數組,數組元素都是數字,想要計算這些元素的平均值和标準差。若使用非函數式程式設計風格的話,如下所示 

  可以使用數組方法map()和reduce()實作同樣的計算,這種實作極其簡潔

  在ES3中,并不包含這些數組方法,需要自定義map()和reduce()函數

  高階函數(higher-order function)指操作函數的函數,它接收一個或多個函數作為參數,并傳回一個新函數

  上面的not()函數就是一個高階函數,因為它接收一個函數作為參數,并傳回一個新函數

  下面的mapper()函數,也是接收一個函數作為參數,并傳回一個新函數,這個新函數将一個數組映射到另一個使用這個函數的數組上

  下面是一個更常見的例子,它接收兩個函數f()和g(),并傳回一個新函數用以計算f(g())

  不完全函數是一種函數變換技巧,即把一次完整的函數調用拆成多次函數調用,每次傳入的實參都是完整實參的一部分,每個拆分開的函數叫做不完全函數,每次函數調用叫做不完全調用。這種函數變換的特點是每次調用都傳回一個函數,直到得到最終運作結果為止

  函數f()的bind()方法傳回一個新函數,給新函數傳入特定的上下文和一組指定的參數,然後調用函數f()。bind()方法隻是将實參放在完整實參清單的左側,也就是說傳入bind()的實參都是放在傳入原始函數的實參清單開始的位置,但有時希望将傳入bind()的實參放在完整實參清單的右側

  利用這種不完全函數的程式設計技巧,可以編寫一些有意思的代碼,利用已有的函數來定義新的函數

  當将不完全調用和其他高階函數整合在一起時,事件就變得格外有趣了。比如,下例定義了not()函數

  可以使用不完全調用的組合來重新組織求平均數和标準差的代碼,這種編碼風格是非常純粹的函數式程式設計

  将上次的計算結果緩存起來,在函數式程式設計中,這種緩存技巧叫做記憶(memorization)。記憶隻是一種程式設計技巧,本質上是犧牲算法的空間複雜度以換取更優的時間複雜度,在用戶端javascript中代碼的執行時間複雜度往往成為瓶頸,是以在大多數場景下,這種犧牲空間換取時間的做法以提升程式執行效率的做法是非常可取的

  memorize()函數建立一個新的對象,這個對象被當作緩存的宿主,并指派給一個局部變量,是以對于傳回的函數來說它是私有的。所傳回的函數将它的實參數組轉換成字元串,并将字元串用做緩存對象的屬性名。如果在緩存中存在這個值,則直接傳回它;否則,就調用既定的函數對實參進行計算,将計算結果緩存起來并傳回

  寫一個遞歸函數時,往往需要實作記憶功能,我們更希望調用實作了記憶功能的遞歸函數,而不是原遞歸函數

  下面利用連續調用單參函數來實作一個簡易的加法運算

  如果完全按照上面實作,則無法實作,因為add(1)(2)如果傳回3,add(1)(2)(3)必然報錯。于是,有以下兩種變形方法

  第一種變形如下:

  第二種變形如下:

繼續閱讀