[英]How to convert OOP to FP in JavaScript
我在 JavaScript 中有這個代碼
class StringFormatter {
init(locale) {
this.locale = locale;
}
getA() {
return 'A' + this.locale
}
getB() {
return 'B' + this.locale
}
getC() {
return 'C' + this.locale
}
}
// when page just render
let stringManager = new StringFormatter();
stringManager.init('enUS');
// call it in other components
let result = stringManager.getA();
所以當我使用這個 OOP class 時,我只需要在頁面第一次渲染時通過初始化locale
一次區域設置。 然后我調用方法getA, getB or getC
的所有后續代碼都將適應這個locale
。
現在,當我想將其轉移到更函數式編程的方式時,我該如何編寫它但仍保持相同的概念。 現在我只能想出這個解決方案
getA(locale) {
return 'A' + locale
}
getB(locale) {
return 'B' + locale
}
getC(locale) {
return 'C' + locale
}
let result = getA('enUS');
但這要求我每次調用 function getA, getB or getC.
時都必須通過locale
。
無論如何,我們仍然可以以某種方式配置語言環境,但只能配置一次,不需要每次都將它傳遞給getA, getB or getC
function,並且不需要以 OOP 方式編寫?
使用閉包:
let getA;
let getB;
let getC;
function initStringFormatter (x) {
let locale = x;
getA = function () { return 'A' + locale }
getB = function () { return 'B' + locale }
getC = function () { return 'C' + locale }
}
initStringFormatter('enUS');
getA();
理論上,閉包可以提供與 OOP 中的 object 屬性/變量/成員完全相同的功能。 閉包和對象之間存在精確的一對一關系。 唯一的區別是閉包使用 scope 作為將變量附加到函數的機制,而對象使用綁定將變量附加到方法。
當然,將 OOP 與 FP 混合並沒有錯。 甚至 Lisp 也有一個 OOP 庫。 所以一個常見的 javascript 習慣用法是返回一個 object 而不是將 function 名稱作為全局變量:
function initStringFormatter (locale) { // another trick is to just use
// the argument directly instead of
// creating another variable.
function a () { return 'A' + locale }
function b () { return 'B' + locale }
function c () { return 'C' + locale }
return {
getA: a,
getB: b,
getC: c
}
}
let stringManager = initStringFormatter('enUS');
stringManager.getA();
的確。 javascript 中的 FP 程序員經常以這種方式使用對象:簡單地作為函數的命名空間。 State 管理可以使用閉包而不是 object 屬性 100% 完成。
Side note: FP is powerful enough that languages like Lisp don't need OOP to be built-in to the language, instead OOP is a design pattern and/or a library - the standard OOP library for Lisp is CLOS: the Common Lisp Object系統
使用閉包的唯一缺點是所有閉包本質上都是私有的(從技術上講,它們不是私有的,它們只是函數的局部變量 - 私有/公共是綁定概念,全局/局部是 scope 概念)。 但是,如果您已經完成了大量的 OOP,您會認為這是 OOP 世界中的良好做法,即永遠不要讓公眾訪問變量。 當使用閉包時,100% 的變量訪問需要通過函數來完成——同樣,在 OOP 世界中,這將是常見的 getter/setter 設計模式,被認為是良好的實踐。
但是請注意,這不是 100% 純 FP。 但這沒關系。 還有其他一些不純的 FP 語言。 Javascript 仍然允許修改封閉的變量。 然而,上面的 function 是純 FP,因為無法修改locale
變量。
制作 javascript 100% 純 FP 很簡單。 只需在代碼中禁止所有使用let
和var
並始終使用const
。 一開始感覺很奇怪,但是您可以只使用const
和 function arguments (默認情況下是常量)來編寫任何程序。 像 javascript 和 Go 這樣的語言讓您可以使用變量以直接的方式管理基於 state 的邏輯。
你可能想要一個thunk?
thunk 延遲計算直到需要其結果,提供 arguments 的惰性評估。 ( ramda 的 thunk 定義)
const thunkGetFromLocale = (locale) => () => (toGet) => toGet + locale let thunk = thunkGetFromLocale('en-US')() console.log(thunk('A')) console.log(thunk('B')) console.log(thunk('C')) thunk = thunkGetFromLocale('vi-VN')() console.log(thunk('A')) console.log(thunk('B')) console.log(thunk('C'))
您可以在 function 之外輕松創建 state:
let locale = 'en-US';
function getA() {
return 'A' + locale
}
但是即使您現在正在使用函數,我也不確定它是否可以稱為“函數式”。
如果您在 function 之外修改 state,則您的行為幾乎與 class 再次相似,除了現在它在 function 之外。
如果您接受函數式編程,理想情況下 function 的結果不應依賴於副作用。
所以另一種處理方法是使用更高階的 function:
function getAForLocale(locale) {
return () => {
return 'A' + locale;
}
}
現在我們可以調用:
const getA = getAForLocale('en-US');
然后我們可以多次重復使用 getA 。
我認為這是最實用的方法,但我不能說這比使用類更好。
擴展@slebetman 的答案,我們可以使用現代 Javascript 功能使我們的閉包更加簡潔。
const initStringFormatter = locale => ({
getA: () => 'A' + locale,
getB: () => 'B' + locale,
getC: () => 'C' + locale
});
let stringManager = initStringFormatter('enUS');
stringManager.getA();
您可以設置一個全局默認值,您可以將其作為每個 function 中的locale
的默認值傳遞。
更新:如果你想改變defaultLocale
; 將其聲明為let
,否則保留為const
。
let defaultLocale = 'enUS'; const getA = (locale = defaultLocale) => 'A' + locale; const getB = (locale = defaultLocale) => 'B' + locale; const getC = (locale = defaultLocale) => 'C' + locale; let result = getA(); console.log(result); // Output: AenUS defaultLocale = 'fr'; // Change the default locale console.log(getB()); // Output: Bfr
更新:如果您想按照 hgb123 的建議將 go thunk-route 傳遞給 thunk,您可以將 function 傳遞到 thunk 中。
const getA = (locale) => 'A' + locale; const getB = (locale) => 'B' + locale; const getC = (locale) => 'C' + locale; const thunkFormatLocale = locale => () => func => func(locale); let thunk = thunkFormatLocale('enUS')(); console.log(thunk(getA)); console.log(thunk(getB)); console.log(thunk(getC)); thunk = thunkFormatLocale('fr')(); console.log(thunk(getA)); console.log(thunk(getB)); console.log(thunk(getC));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.