JS函數式編程小記

JS函數式編程思想

  • 維基百科定義:函數式編程(英語:functional programming),又稱泛函編程,是一種編程範式,它將電腦運算視爲數學上的函數計算,而且避免使用程序狀態以及易變對象。

我的以爲這個定義肥腸生動形象了。
函數式編程到處體現着數學的邏輯思想,經過減小外部依賴來避免外部變量的改變對於程式內部狀態的改變,使得程序變得簡易、聲明式、易於維護。
這裏推薦一下F大的《JS函數式編程指南》,系統介紹了函數式編程的思想。
中文版電子書下載地址走你:https://llh911001.gitbooks.io...
本文章是本身閱讀後的一些理解和記錄~html

First Class Funtcion

這個概念一樣出於書中。規定了變量能夠取的值得範圍,以及該類型的值能夠進行的操做。根據類型的值的可賦值情況,能夠把類型分爲三類。git

  1. 一級的(first class)。該等級類型的值能夠傳給子程序做爲參數,能夠從子程序裏返回,能夠賦給變量。大多數程序設計語言裏,整型、字符類型等簡單類型都是一級的。
  2. 二級的(second class)。該等級類型的值能夠傳給子程序做爲參數,可是不能從子程序裏返回,也不能賦給變量。
  3. 三級的(third class)。該等級類型的值連做爲參數傳遞也不行。

在scala中,函數是能夠做爲參數來傳遞而且返回的,因此scala中的函數就是first class function
PS:scala 是一門多範式(multi-paradigm)的編程語言,設計初衷是要集成面向對象編程和函數式編程的各類特性。
所以,function做爲變量的一種,能夠被儲存,看成參數傳遞,或者被複制給變量等等...程序員

Pure Function

首先弄清純函數的概念:Pure function 意指相同的輸入,永遠會獲得相同的輸出,並且沒有任何顯著的反作用。
它符合兩個條件:編程

  1. 此函數在相同的輸入值時,老是產生相同的輸出。函數的輸出和當前運行環境的上下文狀態無關。
  2. 此函數運行過程不影響運行環境,也就是無反作用(如觸發事件、發起http請求、打印/log等)。

簡單來講,也就是當一個函數的輸出不受外部環境影響,同時也不影響外部環境時,該函數就是純函數,也就是它只關注邏輯運算和數學運算,同一個輸入總獲得同一個輸出。
好比slicesplice,可是slice是純函數,而splice不是,由於後者會改變源數據。
這就是強調使用純函數的緣由,由於純函數相對於非純函數來講,在可緩存性、可移植性、可測試性以及並行計算方面都有着巨大的優點。數組

看一下書中的例子:緩存

// impure
var minimum = 21;

var checkAge = function(age) {
  return age >= minimum;
};

// pure
var checkAge = function(age) {
  var minimum = 21;
  return age >= minimum;
};

在impure 的版本中,checkAge 的結果將取決於 minimum 這個變量,換句話說,除了輸入之外,它將取決於系統狀態,一旦引用了外部環境,它就不符合pure了。
固然,也能夠直接凍結變量,使得狀態再也不變化,那麼它也是一個純函數:less

var immutableState = Object.freeze({
  minimum: 21,
});

Curry(柯里化)

書中定義柯里化的概念是:你能夠只透過部分的參數呼叫一個 function,它會回傳一個 function 去處理剩下的參數。
函數柯里化是一種「預加載」函數的能力,經過傳遞一到兩個參數調用函數,就能獲得一個記住了這些參數的新函數。從某種意義上來說,這是一種對參數的緩存,是一種很是高效的編寫函數的方法,將一個低階函數轉換爲高階函數的過程就叫柯里化。編程語言

//舉個栗子
var checkage = min => (age => age > min);
var checkage18 = checkage(18);
checkage18(20);
// =>true
var curry = require('lodash').curry;

//柯里化兩個純函數
var match = curry((what, str) => str.match(what));
var filter = curry((f, ary) => ary.filter(f));

//判斷字符串裏有沒有空格
var hasSpaces = match(/\s+/g);

hasSpaces("hello world");  // [ ' ' ]
hasSpaces("spaceless");  // null

var findSpaces = filter(hasSpaces);

findSpaces(["tori_spelling", "tori amos"]);  // ["tori amos"]

Compose—Functional飼養

假設要對某個字符串作一系列操做,咱們作的事情一多,嵌套的層數會很是深。相似於巢狀堆積(好比c(b(a())))這種堆砌方式很是不直觀,當咱們但願代碼以平行方式(書中成爲左傾)組合執行時,就成爲Compose
compose 的概念直接來自於數學課本,因此compose都有的一個特性:ide

// 結合律(associativity)
var associative = compose(f, compose(g, h)) == compose(compose(f, g), h);
// true

PointFree

pointfree 模式指的是,永遠沒必要說出你的數據。它的意思是說,函數無須說起將要操做的數據是什麼樣的函數式編程

// 非 pointfree,因為我們提到資料:word
var snakeCase = function(word) {
  return word.toLowerCase().replace(/\s+/ig, '_');
};

// pointfree
var snakeCase = compose(replace(/\s+/ig, '_'), toLowerCase);

這種風格可以幫助咱們減小沒必要要的命名,讓代碼保持簡潔和通用。
固然,爲了在一些函數中寫出Point Free的風格,在代碼的其它地方必然是不那麼Point Free的,這個地方須要本身取捨。

聲明式與宣告式代碼

// 命令式
var makes = [];
for (var i = 0; i < cars.length; i++) {
  makes.push(cars[i].make);
}
// 宣告式
var makes = cars.map(function(car) { return car.make; });

命令式代碼:命令「機器」如何去作事情(how),這樣無論你想要的是什麼(what),它都會按照你的命令實現。
聲明式代碼:告訴「機器」你想要的是什麼(what),讓機器想出如何去作(how)。
命令式的循環要求你必須先實例化一個數組,並且執行完這個實例化語句以後,解釋器才繼續執行後面的代碼。而後再直接迭代 cars 列表,手動增長計數器,就像你開了一輛零部件所有暴露在外的汽車同樣。這不是優雅的程序員應該作的。

聲明式的寫法是一個表達式,如何進行計數器迭代,返回的數組如何收集,這些細節都隱藏了起來。它指明的是作什麼,而不是怎麼作。除了更加清晰和簡潔以外,map 函數還能夠進一步獨立優化,甚至用解釋器內置的速度極快的 map 函數,這麼一來咱們主要的業務代碼就無須改動了

相關文章
相關標籤/搜索