淺析 FP:JavaScript 中的純函數
- 2020 年 4 月 7 日
- 筆記
前言
純函數 是一個常見的概念,在日常工作中也經常會遇到,它其實非常簡單,今天我們來了解一下它的好處以及為什麼要使用它。
兩個特點
一個函數,如果符合以下兩個特點,那麼它就可以稱之為 純函數:
- 對於相同的輸入,永遠得到相同的輸出
- 沒有任何可觀察到的副作用
相同輸入得到相同輸出
我們先來看一個不純的反面典型:
let greeting = 'Hello' function greet (name) { return greeting + ' ' + name } console.log(greet('World')) // Hello World
上面的程式碼中,greet('World'),是不是永遠返回 Hello World ? 顯然不是,假如我們修改 greeting 的值,就會影響 greet 函數的輸出。即函數 greet 其實是 依賴外部狀態 的。
那我們做以下修改:
function greet (greeting, name) { return greeting + ' ' + name } console.log(greet('Hi', 'Savo')) // Hi Savo
將 greeting 參數也傳入,這樣對於任何輸入參數,都有與之對應的唯一的輸出參數了,該函數就符合了第一個特點。
沒有副作用
副作用的意思是,這個函數的運行,不會修改外部的狀態。
下面再看反面典型:
const user = { username: 'savokiss' } let isValid = false function validate (user) { if (user.username.length > 4) { isValid = true } }
可見,執行函數的時候會修改到 isValid 的值(注意:如果你的函數沒有任何返回值,那麼它很可能就具有副作用!)
那麼我們如何移除這個副作用呢?其實不需要修改外部的 isValid 變數,我們只需要在函數中將驗證的結果 return 出來:
const user = { username: 'savokiss' } function validate (user) { return user.username.length > 4 } const isValid = validate(user)
這樣 validate 函數就不會修改任何外部的狀態了~
為什麼要用純函數?
你可能聽過 純函數 有不少優點,如果你經手過各種難維護的函數,你就更應該考慮使用 純函數。
可測試性(Testable)
讓我們先用不純的 greet 方法來做單元測試:
// jest 語法 describe('greet', function() { it('shows a greeting', function() { expect(greet('Savo')).toEqual('Hello Savo') }) })
如果我們修改了 greeting 變數為 Hi,上面的測試就會失敗了,這本質上不應該發生。
那我們如果換成純函數版本的 greet ,所有都是那麼自然~ 只需要修改單元測試中傳入的參數即可!
可快取性(Cacheable)
純函數可以根據輸入來做快取。實現快取的是一種叫做 memorize 的技術。
下面的程式碼來自 Vue 源碼:
/** * Create a cached version of a pure function. * 只適用於快取 接收一個字元串為參數的 fn */ export function cached (fn) { const cache = Object.create(null) return function cachedFn (str) { const hit = cache[str] return hit || (cache[str] = fn(str)) } } /** * Capitalize a string. */ export const capitalize = cached((str) => { return str.charAt(0).toUpperCase() + str.slice(1) })
capitalize 即為快取後的函數,如果多次調用就會返回快取後的值,從而節省計算資源,而這一切的前提都建立在傳入 cached 中的那個函數為純函數的基礎上。
可移植性 / 自文檔化(Portable / Self-Documenting)
由於純函數是自給自足的,它需要的東西都在輸入參數中已經聲明,所以它可以任意移植到任何地方。
並且純函數對於自己的依賴是 誠實的,這一點你看它的 形參 就知道啦~正所謂 形參起的好,注釋不用搞~(雙押!)純函數就是這麼個正直的小可愛~
總結
好啦,我們已經大概了解了純函數,它對於我們寫出良好程式碼有著重要的意義,同時也是函數式編程中的精髓。本來本篇是想單純介紹純函數的,但是想起來了柯里化 (curry) 也沒有講過,那麼下次有機會就講一講柯里化吧~
We』ll see!
參考鏈接
- What is a Pure Function
- JS 函數式編程指南中文版

