【发布时间】:2020-04-05 17:05:03
【问题描述】:
我正在研究如何在 javascript 中应用函数式编程,并且正在尝试避免使用 class 关键字。
我不喜欢仅仅为了遵循某种范式而采取极端措施,但我很想知道是否可以在不使用类的情况下编写好的代码。
到目前为止,我主要使用函数取得了成功,但有一种情况我无法真正弄清楚。
当我们想要在不同对象之间重用行为时,我们通常(在 OOP 中)创建一个扩展另一个类的类。
class FlyingThing {
private let _isFlying = false
fly() {
_isFlying = true
return this
}
land() {
_isFlying = false
return this
}
isFlying() {
return _isFlying
}
}
class Duck extends FlyingThing {
quack() {
return 'Quack!'
}
}
const duck = new Duck()
console.log(duck.fly().quack())
现在到函数式方法...
示例来自:https://medium.com/javascript-scene/functional-mixins-composing-software-ffb66d5e731c
const flying = o => {
let isFlying = false
return Object.assign({}, o, {
fly () {
isFlying = true
return this
},
isFlying: () => isFlying,
land () {
isFlying = false
return this
}
})
}
const quacking = quack => o => Object.assign({}, o, {
quack: () => quack
})
const createDuck = quack => quacking(quack)(flying({}))
const duck = createDuck('Quack!')
console.log(duck.fly().quack())
好的,我喜欢这个主意;我们正在使用组合,并且我们没有任何父母和孩子之间的紧密耦合。很酷。
但是,通常当我们使用类时,子级可以访问父级的成员,并且可能需要在某些方法中使用它。例如:
class FlyingThing {
private let _isFlying = false
fly() {
_isFlying = true
return this
}
land() {
_isFlying = false
return this
}
isFlying() {
return _isFlying
}
}
class Duck extends FlyingThing {
quack() {
return 'Quack!'
}
// New method - Depends on 'isFlying' defined in parent
layEgg() {
if(isFlying) return
return 'Laying egg...'
}
}
const duck = new Duck()
console.log(duck.fly().quack())
所以问题是,我们如何仅使用函数优雅地解决这个问题?
【问题讨论】:
-
你的错误是试图用函数式编程的方式重建面向对象的习语。使用 FP 表示功能性习语。
-
@bob 这就是我所怀疑的。您对在这种情况下如何以更多的 FP 术语进行思考有什么建议吗?
-
不要试图避开
class关键字。尽量避免可变的对象属性(即,让一切都成为readonly,因为这看起来像 Typescript)。然后,一旦您熟悉了不可变对象,您就会发现可能使class语法的替代方案更具吸引力的模式。 -
FP 使用algebraic data types 对域进行建模。如果您有 FP 背景,这是一种完全不同的方法,而且相当陌生。
-
"当我们有想要在不同对象之间重用的行为时,我们通常(在 OOP 中)创建一个扩展另一个类的类。" 不,我们 不要。至少我们不应该。如果你想重用代码,那就让它成为一个可重用的组件。使用组合。继承不是为了代码重用,而是为了构建你的类层次结构——“什么是什么”,而不是“什么做什么”。
标签: javascript class oop mixins composition