首先,函數式編程是一種編程範型。其餘的編程範型有面向過程編程(關注計算機的執行步驟,指定先作這個再作那個),還有面向對象(建立對象而後對象又有方法而後又能夠改變它們)等等。函數式編程也是編程範型,函數爲王。**javascript
這裏須要介紹下編程範型的概念,編程範型/編程範式(範即模範、典範之意,範型即模式、方法)是一類典型的編程風格,是指從事軟件工程的一類典型的風格。如函數式編程、過程式編程、面向對象編程、指令式編程等等爲不一樣的編程範型。
編程範型提供了(同時決定了)程序員對程序執行的見解。例如,在面向對象編程中,程序員認爲程序是一系列相互做用的對象,而在函數式編程中一個程序會被看做是一個無狀態的函數計算的序列。html
函數式編程也是一種編程風格,完成項目時怎樣組織編寫代碼。
函數式編程更是一種觀念,能夠以一種思考問題的方式來完成任務,也是一種趨勢。前端
要用函數實現程序,給定輸入,會有一個輸出,咱們更多的要考慮數據的輸入輸出流,須要想怎樣用函數表達一切。java
const name = "ming"; const greeting = "hello,"; console.log(greeting + name); // hello,ming
function greet(name) { return `hello,${name}`; } greet("ming"); // hello,ming
反作用:反作用是指在計算過程當中,系統狀態的一種變化,或者是與外部進行的可觀察的交互。
反作用可能包含,但不限於:git
函數式編程的哲學就是假定反作用是形成不正當行爲的主要緣由。
固然這並非說,要禁止使用一切反作用,而是說,要讓它們在可控的範圍內發生。程序員
純函數:針對相同的一組輸入,會永遠獲得相同的輸出,並且沒有任何可觀察的反作用,這樣的函數叫純函數。
用一個例子來講明下。數組裏的slice和splice方法,這兩個函數的做用是同樣的,可是須要注意的是,splice會改變原數組,而slice不會改變原數組。因此slice每次用相同的輸入去執行都會獲得相同的輸出,符合純函數的定義。而splice改變了原數組,每次用相同的輸入去執行都會獲得不一樣的輸出,產生了反作用。github
let arr = [1,2,3,4,5]; // 純的 arr.slice(0,3); // [1,2,3] arr.slice(0,3); // [1,2,3] arr.slice(0,3); // [1,2,3] // 不純的 arr.splice(0,3); // [1,2,3] arr.splice(0,3); // [4,5] arr.splice(0,3); // []
再看另一個例子:數據庫
// 不純的 var b = 21; var test = function(a) { return a >= b; }; // 純的 var test = function(a) { var b = 21; return a >= b; };
在不純的版本中,函數外部的變量會影響到函數的返回結果,或者說它引入了外部的環境。
而在純的版本中,函數就能夠本身保持「獨立」。編程
function makeAdjectifier(adjective) { return function (string) { adjective + " " + string; }; } var coolifier = makeAdjectifier("cool"); coolifer("conference"); // => "cool conference"
不要使用for/while等迭代,使用map,reduce,filter這樣的高階函數,能夠做爲一個函數去應用。數組
很差的作法:
let arr = ['1','2','3']; arr[2] = '4'; console.log(arr); // ['1,'2','4']
好的作法:
const arr = ['1','2','3']; const newArr = arr.map(item => { if(item === '3') { return '4'; } return item; }); console.log(arr); // ['1,'2','3'] console.log(newArr); // ['1,'2','4']
當咱們將事物視爲不變時,咱們最終所作的事情就是爲全部事物製做新的副本。
若是有一個數組[1,2,3],咱們要改變3爲4,那麼在可變的環境中直接把3換爲4便可,可是以前說過要避免這種操做,就要取而代之,複製一個新數組,複製1,2而後輸入4,就須要花更多時間存儲兩個數組,這是很是可怕的。咱們能夠把這個數組當作一棵樹,樹上的葉子結點就是要存儲東西的地方,若是想改變某個數據,只須要製造一個新節點存4,能夠新建一個有1,2,4的樹,能夠服用以前已經存在的數據。這個叫作數據共享。這樣咱們能夠不用浪費不少時間和存儲空間去更新數據。
概念:只傳遞給函數一部分參數來調用它,讓它返回一個函數去處理剩下的參數。
var add = function(x) { return function(y) { return x + y; }; }; var increment = add(1); var addTen = add(10); increment(2); // 3 addTen(2); // 12
上面咱們定義了一個add函數,它接收一個參數,並返回一個新的函數。調用add後,返回的函數就以閉包的方式記住了它的第一個參數x。
組合其實就是你能夠選擇兩個函數,讓它們結合,產生一個新的函數。這樣寫可使咱們的代碼更加優雅。
var compose = function(f,g) { return function(x) { return f(g(x)); }; };
f 和 g 都是函數,x 是在它們之間經過「管道」傳輸的值。
var toUpperCase = function(x) { return x.toUpperCase(); }; var exclaim = function(x) { return x + '!'; }; var shout = compose(exclaim, toUpperCase); shout("send in the clowns"); //=> "SEND IN THE CLOWNS!"
參考:
Learning Functional Programming with JavaScript, by Anjana Vakil — JSUnconf 2016
函數式編程初探
編程範型wiki
函數式編程指北中文版
更多文章以及分享請關注微信公衆號 前端er的分享,不止於前端,按期輸出一些技術知識、生活感想、理財知識等。