- 簡述
- 無副作用(No Side Effects)
- 高階函數(shù)(High-Order Function)
- 柯里化(Currying)
- 閉包(Closure)
- 不可變(Immutable)
- 惰性計算(Lazy Evaluation)
- Monad
一等公民
? ? ? ?高階函數(shù)(High-Order Function)是函數(shù)式編程思維中的重要條件,而滿足該條件的編程語言則需要將函數(shù)作為該語言的一等公民來看待。符合一等公民的條件是:
- 函數(shù)可以作為一種數(shù)據(jù)類型的值,賦值于一個變量;
- 函數(shù)可以作為參數(shù),在其他函數(shù)中進(jìn)行傳遞;
- 函數(shù)可以作為返回值,在其他函數(shù)中返回;

? ? ? ?將函數(shù)視作一等公民的語言有:JavaScript、Golang、Python、Scala、Lua、Lisp、Scheme等。同時,有著越來越多的其他語言看上了函數(shù)式編程的出彩之處,以其特有的方式實現(xiàn)著符合自身編程方式的高階函數(shù)。
函數(shù)類型
? ? ? ?在 JavaScript 中,函數(shù)是數(shù)據(jù)類型 object 的子類型——即是指一種對象類型。我們可以通過 typeof 操作符檢測一個值是否是一個函數(shù)類型:
// 一個函數(shù)聲明
function foo(x){
return x + 10;
}
typeof foo; // 返回值:'function'
然而,實際上在 JavaScript 中,函數(shù)并非是基本的數(shù)據(jù)類型,函數(shù)隸屬于對象類型。能夠使用 typeof 操作符進(jìn)行檢測僅僅是語言提供的便利:
// 聲明一個 number 類型的變量
const n = 13;
// 一個函數(shù)聲明
function foo(x){
return x + 10;
}
typeof foo; // 結(jié)果:'function'
typeof n; // 結(jié)果:'number'
// 檢測 n 所持有的值是否是對象
n instanceof Object; // 結(jié)果:false
// 檢測 foo 所持有的值是否是對象
foo instanceof Function; // 結(jié)果:true
Function instanceof Object; // 結(jié)果:true
foo instanceof Object; // 結(jié)果:true
可見,函數(shù)是一個隸屬于 Function 的對象,而 Function 本身又隸屬于頂層 Object ,是它的子對象。因此,一個函數(shù)的實例,也隸屬于 Object ,他們之間擁有間接的繼承關(guān)系。
? ? ? ?很多有 面向?qū)ο?/code> 經(jīng)驗的同學(xué)可能會想,實例化對象不是通過 new 關(guān)鍵字調(diào)用類的構(gòu)造函數(shù)來進(jìn)行的嗎?這個問題很簡單:拿 Java 例舉,Java 中擁有字面量形式的對象聲明方式 String str = "I like Java!";,聲明變量 str 的過程中實際上也構(gòu)建了一個字符串對象,并沒有顯式的使用關(guān)鍵字 new 。
? ? ? ?在 JavaScript 中,以字面量的方式構(gòu)建對象有很多種,比如:
// 數(shù)組字面量
const arr = [1, 2, 3];
// 對象字面量
const obj = {id : 'xx001'};
// 函數(shù)字面量
function foo(){} // 函數(shù)聲明
const bar = function(){} // 函數(shù)表達(dá)式
const baz = (x) => x + 3; // ES6提供的lambda表達(dá)式函數(shù)
函數(shù)入?yún)?/h3>
? ? ? ?由于函數(shù)也是一個對象,因此函數(shù)也可以作為其他函數(shù)的參數(shù),作為入?yún)ⅲ?/p>
// 一個函數(shù)聲明
const add2 = (x) => x + 2; // 參數(shù)x基礎(chǔ)上加2的函數(shù)
const sub2 = (x) => x - 2; // 參數(shù)x基礎(chǔ)上減2的函數(shù)
const result = (y, f) => y * f(y); // 參數(shù)y基礎(chǔ)上乘以 函數(shù)參數(shù)f 的運算結(jié)果
result(4, add2); // 結(jié)果: 24
result(4, sub2); // 結(jié)果: 8
? ? ? ?在 JavaScript 內(nèi)置對象中,也有非常多的函數(shù)入?yún)嵗热?Array.prototype.map 函數(shù)的簡單實現(xiàn):
// 一個高仿的 Array.prototype.map 函數(shù)
const map = function(arr, f){
const t = [];
for(let i=0;i<arr.length; t.push(f(arr[i],i++,arr)));
return t;
}
// 聲明一個數(shù)組
const a1 = [1,2,3,4];
// map函數(shù)調(diào)用:參數(shù) f 所示為每一個值乘以3,并且返回一個新數(shù)組
const a2 = map(a1, (e) => e*3); // 結(jié)果:[3, 6, 9, 12]
函數(shù)返回值
? ? ? ?函數(shù)可以作為參數(shù),當(dāng)然也可以作為返回值。作為返回值的函數(shù),常用于緩存上一個函數(shù)的執(zhí)行狀態(tài):
// 一個函數(shù)聲明
const add2 = (x) => x + 2; // 參數(shù)x基礎(chǔ)上加 2 的函數(shù)
const mul2 = (x) => x * 2; // 參數(shù)x基礎(chǔ)上乘以 2 的函數(shù)
// 該函數(shù)用于計算,計算結(jié)果不會直接得出,而是緩存了用于計算的兩個函數(shù)
const calc = function(f1, f2){
return (y) => f1( f2(y) );
}
// 根據(jù)緩存順序不同,生成的新的函數(shù)執(zhí)行過程也不同
const c01 = calc(add2, mul2);
c01(3); // 結(jié)果: 8
const c02 = calc(mul2, add2);
c02(3); // 結(jié)果:10