JS 函數語言程式設計思維簡述(二):高階函式
- ofollow,noindex">簡述
- 無副作用(No Side Effects)
- 高階函式(High-Order Function)
- 科裡化(Currying)
- 閉包(Closure)
- 不可變(Immutable)
- 惰性計算(Lazy Evaluation)
- Monad
一等公民
高階函式(High-Order Function)是函數語言程式設計思維中的重要條件,而滿足該條件的程式語言則需要將函式作為該語言的 一等公民 來看待。符合 一等公民 的條件是:
- 函式可以作為一種資料型別的值,賦值於一個變數;
- 函式可以作為引數,在其他函式中進行傳遞;
- 函式可以作為返回值,在其他函式中返回;

image
將函式視作一等公民的語言有:JavaScript/">JavaScript、Golang、Python、Scala、Lua、Lisp、Scheme等。同時,有著越來越多的其他語言看上了函數語言程式設計的出彩之處,以其特有的方式實現著符合自身程式設計方式的 高階函式 。
函式型別
在 JavaScript
中,函式是資料型別 object
的子型別——即是指一種物件型別。我們可以通過 typeof
操作符檢測一個值是否是一個函式型別:
// 一個函式宣告 function foo(x){ return x + 10; } typeof foo; // 返回值:'function'
然而,實際上在 JavaScript
中,函式並非是 基本的資料型別 ,函式隸屬於物件型別。能夠使用 typeof
操作符進行檢測僅僅是語言提供的便利:
// 宣告一個 number 型別的變數 const n = 13; // 一個函式宣告 function foo(x){ return x + 10; } typeof foo; // 結果:'function' typeof n; // 結果:'number' // 檢測 n 所持有的值是否是物件 n instanceof Object; // 結果:false // 檢測 foo 所持有的值是否是物件 foo instanceof Function; // 結果:true Function instanceof Object; // 結果:true foo instanceof Object; // 結果:true
可見,函式是一個隸屬於 Function
的物件,而 Function
本身又隸屬於頂層 Object
,是它的子物件。因此,一個函式的例項,也隸屬於 Object
,他們之間擁有間接的繼承關係。
很多有 面向物件
經驗的同學可能會想,例項化物件不是通過 new
關鍵字呼叫類的建構函式來進行的嗎?這個問題很簡單:拿 Java
例舉, Java
中擁有字面量形式的物件宣告方式 String str = "I like Java!";
,宣告變數 str
的過程中實際上也構建了一個字串物件,並沒有顯式的使用關鍵字 new
。
在 JavaScript
中,以字面量的方式構建物件有很多種,比如:
// 陣列字面量 const arr = [1, 2, 3]; // 物件字面量 const obj = {id : 'xx001'}; // 函式字面量 function foo(){}// 函式宣告 const bar = function(){}// 函式表示式 const baz = (x) => x + 3;// ES6提供的lambda表示式函式
函式入參
由於函式也是一個物件,因此函式也可以作為其他函式的引數,作為入參:
// 一個函式宣告 const add2 = (x) => x + 2;// 引數x基礎上加2的函式 const sub2 = (x) => x - 2;// 引數x基礎上減2的函式 const result = (y, f) => y * f(y);// 引數y基礎上乘以 函式引數f 的運算結果 result(4, add2);// 結果: 24 result(4, sub2);// 結果: 8
在 JavaScript
內建物件中,也有非常多的函式入參例項,比如 Array.prototype.map
函式的簡單實現:
// 一個高仿的 Array.prototype.map 函式 const map = function(arr, f){ const t = []; for(let i=0;i<arr.length; t.push(f(arr[i],i++,arr))); return t; } // 宣告一個數組 const a1 = [1,2,3,4]; // map函式呼叫:引數 f 所示為每一個值乘以3,並且返回一個新陣列 const a2 = map(a1, (e) => e*3); // 結果:[3, 6, 9, 12]
函式返回值
函式可以作為引數,當然也可以作為返回值。作為返回值的函式,常用於快取上一個函式的執行狀態:
// 一個函式宣告 const add2 = (x) => x + 2;// 引數x基礎上加 2 的函式 const mul2 = (x) => x * 2;// 引數x基礎上乘以 2 的函式 // 該函式用於計算,計算結果不會直接得出,而是快取了用於計算的兩個函式 const calc = function(f1, f2){ return (y) => f1( f2(y) ); } // 根據快取順序不同,生成的新的函式執行過程也不同 const c01 = calc(add2, mul2); c01(3); // 結果: 8 const c02 = calc(mul2, add2); c02(3); // 結果:10