繁体   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