[英]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.