JS 函數(shù)式編程思維簡述(二):高階函數(shù)

  1. 簡述
  2. 無副作用(No Side Effects)
  3. 高階函數(shù)(High-Order Function)
  4. 柯里化(Currying)
  5. 閉包(Closure)
  6. 不可變(Immutable)
  7. 惰性計算(Lazy Evaluation)
  8. Monad

一等公民

? ? ? ?高階函數(shù)(High-Order Function)是函數(shù)式編程思維中的重要條件,而滿足該條件的編程語言則需要將函數(shù)作為該語言的一等公民來看待。符合一等公民的條件是:

  • 函數(shù)可以作為一種數(shù)據(jù)類型的值,賦值于一個變量;
  • 函數(shù)可以作為參數(shù),在其他函數(shù)中進(jìn)行傳遞;
  • 函數(shù)可以作為返回值,在其他函數(shù)中返回;
image

? ? ? ?將函數(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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容