主要的編程范式有三種:命令式編程钙姊,聲明式編程和函數(shù)式編程。
命令式編程:
命令式編程的主要思想是關注計算機執(zhí)行的步驟,即一步一步告訴計算機先做什么再做什么边灭。
比如:如果你想在一個數(shù)字集合 collection(變量名) 中篩選大于 5 的數(shù)字,你需要這樣告訴計算機:
第一步健盒,創(chuàng)建一個存儲結果的集合變量 results绒瘦;
第二步,遍歷這個數(shù)字集合 collection扣癣;
第三步:一個一個地判斷每個數(shù)字是不是大于 5惰帽,如果是就將這個數(shù)字添加到結果集合變量 results 中。
很明顯父虑,這個樣子的代碼是很常見的一種该酗,不管你用的是 C, C++ 還是 C#, Java, Javascript, BASIC, Python, Ruby?等等,你都可以以這個方式寫士嚎。
聲明式編程:
聲明式編程是以數(shù)據(jù)結構的形式來表達程序執(zhí)行的邏輯呜魄。它的主要思想是告訴計算機應該做什么,但不指定具體要怎么做莱衩。
SQL 語句就是最明顯的一種聲明式編程的例子爵嗅,例如:
SELECT*FROM collection WHERE num>5
除了 SQL,網(wǎng)頁編程中用到的 HTML 和 CSS 也都屬于聲明式編程笨蚁。
通過觀察聲明式編程的代碼我們可以發(fā)現(xiàn)它有一個特點是它不需要創(chuàng)建變量用來存儲數(shù)據(jù)睹晒。
函數(shù)式編程:
“函數(shù)式編程”, 又稱泛函編程, 是一種”編程范式”(programming paradigm)趟庄,也就是如何編寫程序的方法論,和指令式編程相比伪很,函數(shù)式編程的思維方式更加注重函數(shù)的計算岔激。函數(shù)式編程和聲明式編程是有所關聯(lián)的,因為他們思想是一致的:即只關注做什么而不是怎么做是掰。但函數(shù)式編程不僅僅局限于聲明式編程虑鼎。它的主要思想是把問題的解決方案寫成一系列嵌套的函數(shù)調用。
函數(shù)式編程最重要的特點是“函數(shù)第一位”键痛,即函數(shù)可以出現(xiàn)在任何地方炫彩,比如你可以把函數(shù)作為參數(shù)傳遞給另一個函數(shù),不僅如此你還可以將函數(shù)作為返回值絮短。大部分常見的編程語言一半都已經提供了對這種編程方式的支持江兢,比如 JavaScript,再有 C# 中的 LINQ 和 Java 中的 Lambda 和閉包的概念丁频。
使用指令式編程時杉允,當情況變得更加復雜,表達式的寫法會遇到幾個問題:表意不明顯席里,逐漸變得難以維護復用性差叔磷,會產生更多的代碼量會產生很多中間變量。這個時候使用函數(shù)式編程的優(yōu)勢就提現(xiàn)出來了:
函數(shù)式編程的優(yōu)點
1. 代碼簡潔奖磁,開發(fā)快速
函數(shù)式編程大量使用函數(shù)改基,減少了代碼的重復,因此程序比較短咖为,開發(fā)速度較快秕狰。
// 數(shù)組中每個單詞,首字母大寫
// 一般寫法
const arr = ['apple','pen','apple-pen'];
for(const i in arr){
const c = arr[i][0];
arr[i] = c.toUpperCase() + arr[i].slice(1);
}
console.log(arr);
// 函數(shù)式寫法一
function upperFirst(word){
returnword[0].toUpperCase() + word.slice(1);
}
function wordToUpperCase(arr){
returnarr.map(upperFirst);
}
console.log(wordToUpperCase(['apple','pen','apple-pen']));
// 函數(shù)式寫法二
console.log(arr.map(['apple','pen','apple-pen'], word => word[0].toUpperCase() + word.slice(1)));
2. 接近自然語言躁染,易于理解
函數(shù)式編程的自由度很高鸣哀,可以寫出很接近自然語言的代碼。
將表達式(1 + 2) * 3 - 4吞彤,寫成函數(shù)式語言:subtract(multiply(add(1,2), 3), 4)
對它進行變形我衬,不難得到另一種寫法:add(1,2).multiply(3).subtract(4)
這基本就是自然語言的表達了。再看下面的代碼备畦,大家應該一眼就能明白它的意思吧:
merge([1,2],[3,4]).sort().search("2")
因此低飒,函數(shù)式編程的代碼更容易理解。
3. 更方便的代碼管理
函數(shù)式編程不依賴懂盐、也不會改變外界的狀態(tài)褥赊,只要給定輸入?yún)?shù),返回的結果必定相同莉恼。因此拌喉,每一個函數(shù)都可以被看做獨立單元速那,很有利于進行單元測試(unit testing)和除錯(debugging),以及模塊化組合尿背。
函數(shù)編程的一些基本特點包括:
1. 函數(shù)是”第一等公民”:
????函數(shù)與其他數(shù)據(jù)類型一樣端仰,處于平等地位,可以賦值給其他變量田藐,也可以作為參數(shù)荔烧,傳入另一個函數(shù),或者作為別的函數(shù)的返回值汽久。
2. 高階函數(shù)(Higher Order Function):
? ? 將函數(shù)作為參數(shù)鹤竭,或者是可以將函數(shù)作為返回值的函數(shù)。比如我們平常用到的回調函數(shù).
3. 函數(shù)柯里化(Currying):
? ??把接受多個參數(shù)的函數(shù)變換成接受一個單一參數(shù)(最初函數(shù)的第一個參數(shù))的函數(shù)景醇,并且返回接受余下的參數(shù)且返回結果的新函數(shù)臀稚。
其過程就是消化一個參數(shù),然后生成一個新的函數(shù)三痰,剩余的參數(shù)作為新函數(shù)的參數(shù)吧寺。
// 柯里化之前
function?add(x, y) {
????return?x + y;
}
add(1, 2)?// 3
// 柯里化之后
function?addX(y) {
????return?function?(x) {
????????return?x + y;
????};
}
addX(2)(1)?// 3
4. 懶惰計算(lazy evaluation):
? ??在惰性計算中,表達式不是在綁定到變量時立即計算散劫,而是在求值程序需要產生表達式的值時進行計算稚机。
5. 引用透明性:
指的是函數(shù)的運行不依賴于外部變量或"狀態(tài)",只依賴于輸入的參數(shù)舷丹,任何時候只要參數(shù)相同抒钱,引用函數(shù)所得到的返回值總是相同的。
6. 沒有副作用:
? ??意味著函數(shù)要保持獨立颜凯,所有功能就是返回一個新的值,沒有其他行為仗扬,尤其是不得修改外部變量的值症概。
7. 純函數(shù):
? ??- 其結果只能從它的參數(shù)的值來計算?
????- 不能依賴于能被外部操作改變的數(shù)據(jù)?
????- 不能改變外部狀態(tài)
函數(shù)式編程思維在我們工作中的體現(xiàn)
? ? 1.map/reduce/filter方法
? ? 傳入一個函數(shù)作為參數(shù),返回一個新的數(shù)組早芭,不對原來的數(shù)組進行修改彼城,不使用中間變量,沒有副作用退个。
? ? 2.閉包
????閉包的主要用途就是可以定義一些作用域局限的持久化變量募壕,這些變量可以用來做緩存或者計算的中間量等等。
????下面的例子是一個簡單的緩存工具的實現(xiàn)语盈,匿名函數(shù)創(chuàng)造了一個閉包舱馅,使得?store?對象 ,一直可以被引用刀荒,不會被回收代嗤。
// 簡單的緩存工具
const cache = (function() {
????const store = {};
????return {
????????get(key) {
????????????return store[key];
????????},
????????set(key, val) {
????????????store[key] = val;
????????}
????}
}());
cache.set('a', 1);
cache.get('a'); // 1
????3.鏈式優(yōu)化
? ? 比如回調函數(shù)和promise函數(shù)
// 優(yōu)化寫法 (loadsh 的鏈式寫法)
constutils = {
? chain(a) {
this._temp = a;
returnthis;
? },
? sum(b) {
this._temp += b;
returnthis;
? },
? sub(b) {
this._temp -= b;
returnthis;
? },
? value() {
const_temp =this._temp;
this._temp =undefined;
return_temp;
? }
};
console.log(utils.chain(1).sum(2).sum(3).sub(4).value());
4.連續(xù)箭頭函數(shù)
function add(a) {
? ? return function(b) {
? ? ? ? return a + b
? ? }
}
var add3 = add(3)
add3(4) === 3 + 4 //true
add 函數(shù) 在 es6 里的寫法等價為
let add = a => b => a + b
add(3)(4)
參考文章:
https://github.com/EasyKotlin/chapter8_fp
http://taobaofed.org/blog/2017/03/16/javascript-functional-programing/