# 高階函式 Higher-order function 本文作者學習筆記網站:https://dev.rraiy.cc/ ### 定義:一個能夠接收函式作為參數(這個被接收的函式稱為回調函式)或者將函式作為返回值輸出的函式,滿足其中之一即可 也就是可以操作其他函式的函式~並非只有 JS 擁有,是一個廣泛使用的定義 高階函式聽起來好像有點可怕,但其實我們都已經使用過不少高階函式 *函數作為參數的例子* map → 可傳入一個函式來操作陣列中的每個元素(返回新陣列) filter → 可傳入一個函式來過濾陣列中的每個元素 forEach → 可傳入一個函式來操作陣列中的每個元素(不返回新陣列) *返回值為函數的例子* bind → 可傳入一個 this 的指定對象,並回傳一個新函式綁定該 this --- 先看兩個概念 一級函式( First-class Function )&函式語言程式設計 ### 一級函式( First-class Function ) 當函式在那個語言中可以被視為跟其他的變數一樣時,我們稱那樣的程式語言擁有一級函式,函式與其他類型的地位一樣。例如,函式可以作為參數傳遞到另一個函式,可以被另一個函式作為回傳值且可以被當作值一樣指派到另一個變數。 JavaScript 的函式擁有這些超能力: - 將函式指定到一個變數 - 函式可作為參數來傳遞 - 函式也可以被回傳(return) - 函式是物件 一級函式存在代表高階函式存在 ### 函式語言程式設計(函式編程)Functional Programming * 以函式的形式來思考和設計代碼 - 更易理解、維護 * λ演算為該語言最重要的基礎。而且,λ演算的函式可以接受函式作為輸入參數和輸出返回值。 * 在函式編程中,函式是第一類物件,意思是說一個函式,既可以作為其它函式的輸入參數值,也可以從函式中返回值,被修改或者被分配給一個變數。 * JavaScript 支援函式編程概念 * 在高階函式執行時,由於回調函式的存在,讓高階函式的執行結果不同,非常靈活 :::info 以實際代碼理解高階函式的用法 ::: 首先舉例 map,現在我們希望能將陣列中的每個值變為兩倍 如果不使用高階函式 ```javascript= const arr1 = [1, 2, 3]; const arr2 = []; for(let i = 0; i < arr1.length; i++) { arr2.push(arr1[i] * 2); } console.log(arr2); ``` 使用高階函式 map (用箭頭函式會更簡潔) ```javascript= const arr1 = [1, 2, 3]; const arr2 = arr1.map(function(item) { return item * 2; }); console.log(arr2); ``` 再看一個 filter 的例子,我們要找出年紀大於等於18歲的人 不使用高階函式 ```javascript= const persons = [ { name: 'Peter', age: 16 }, { name: 'Mark', age: 18 }, { name: 'John', age: 27 }, { name: 'Jane', age: 14 }, { name: 'Tony', age: 24}, ]; const fullAge = []; for(let i = 0; i < persons.length; i++) { if(persons[i].age >= 18) { fullAge.push(persons[i]); } } console.log(fullAge); ``` 使用高階函式 ```javascript= const persons = [ { name: 'Peter', age: 16 }, { name: 'Mark', age: 18 }, { name: 'John', age: 27 }, { name: 'Jane', age: 14 }, { name: 'Tony', age: 24}, ]; const fullAge = persons.filter(person => person.age >= 18); console.log(fullAge); ``` :::success !可以看到使用高階函式使代碼變得簡潔 ::: 假設現在沒有 built-in 的 map 方法,試著自己構建一個 ```javascript= const strArray = ['JavaScript', 'Python', 'PHP', 'Java', 'C']; function mapForEach(arr, fn) { // 這裡的 mapForEach 相當於我們熟悉的 map const newArray = []; // 作為 map 返回的新陣列 for(let i = 0; i < arr.length; i++) { // 操作邏輯 newArray.push( fn(arr[i]) // !這邊是高階函式的重點 ); } return newArray; } const lenArray = mapForEach(strArray, function(item) { return item.length; }); console.log(lenArray); // 這是非常簡單範例,沒有考慮錯誤回應、index以及原陣列 ------ // 較為完整的話,可以這樣改 function map(arr, mapCallback) { if (!Array.isArray(arr) || !arr.length || typeof mapCallback !== 'function') { return 'Error'; } else { const result = []; for (let i = 0, len = arr.length; i < len; i++) { result.push(mapCallback(arr[i], i, arr)); } return result; } } ``` 也參考 MDN 的 map Polyfill 的程式邏輯 [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#polyfill](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#polyfill) --- 本文作者學習筆記網站:https://dev.rraiy.cc/ --- 參考資料 [https://www.geeksforgeeks.org/difference-between-first-class-and-higher-order-functions-in-javascript/](https://www.geeksforgeeks.org/difference-between-first-class-and-higher-order-functions-in-javascript/) [https://blog.bitsrc.io/understanding-higher-order-functions-in-javascript-75461803bad](https://blog.bitsrc.io/understanding-higher-order-functions-in-javascript-75461803bad) [https://cythilya.github.io/2017/02/27/currying-in-javascript/](https://cythilya.github.io/2017/02/27/currying-in-javascript/)