簡體   English   中英

如何在 JavaScript 中將 OOP 轉換為 FP

[英]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 很簡單。 只需在代碼中禁止所有使用letvar並始終使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM