功能性 101
惯用的 JavaScript 解决方案是 map-join。但是函数式编程是将程序分解为可重用的模块并创建抽象障碍-
// Task.js
const empty =
{ id: 0, task: "" }
const task = (id = 0, task = "") =>
({ id, task })
const toString = (t = empty) =>
`${t.id}. ${t.task}`
const toStringAll = ([ first, ...rest ]) =>
rest.reduce // <-- reduce
( (r, x) => r + ", " + toString(x)
, toString(first)
)
export { empty, task, toString, toStringAll }
所以有一个使用reduce 的可能且合理的实现。这个程序的可读性很好,因为模块的每个部分都很小并且只做一件事。
现在我们关闭模块上的假想盖子,忘记其中的所有复杂性。剩下的是一个干净的界面,可以清楚地传达模块的功能 -
// Main.js
import { task, toStringAll } from './Task'
const data =
[ task(7, "foo")
, task(22, "bar")
, task(33, "qux")
]
console.log(toStringAll(data))
// 7. foo, 22. bar, 33. qux
console.log(toStringAll(data.slice(0,2)))
// 7. foo, 22. bar
console.log(toStringAll(data.slice(0,1)))
// 7. foo
console.log(toStringAll(data.slice(0,0)))
// 0.
展开下面的sn-p,在浏览器中验证结果-
const empty =
{ id: 0, task: "" }
const task = (id = 0, task = "") =>
({ id, task })
const toString = (t = empty) =>
`${t.id}. ${t.task}`
const toStringAll = ([ first, ...rest ]) =>
rest.reduce
( (r, x) => r + ", " + toString(x)
, toString(first)
)
const data =
[ task(7, "foo")
, task(22, "bar")
, task(33, "qux")
]
console.log(toStringAll(data))
// 7. foo, 22. bar, 33. qux
console.log(toStringAll(data.slice(0,2)))
// 7. foo, 22. bar
console.log(toStringAll(data.slice(0,1)))
// 7. foo
console.log(toStringAll(data.slice(0,0)))
// 0.
抽象的障碍
Main.js 与Task 被抽象屏障隔开。现在我们可以为toString 和toStringAll 选择任何实现,而无需Main 自己关心Task 如何在盖子下运行。
让我们练习进行更改并更新empty 任务的表示方式。在上面的程序中,我们看到0.,但我们会改为使用Empty.。只是为了好玩,让我们尝试toStringAll 的新实现 -
// Task.js
const empty = //
const task = //
const toString = (t = empty) =>
t === empty
? `Empty.` // <-- custom representation for empty
: `${t.id}. ${t.task}`
const toStringAll = ([ t = empty, ...more ]) =>
more.length === 0
? toString(t)
: toString(t) + ", " + toStringAll(more) // <-- recursive
//
export { empty, task, toString, toStringAll }
Main 不需要做任何不同的事情 -
// Main.js
import { task, toStringAll } from './Task'
const data = //
console.log(toStringAll(data))
// 7. foo, 22. bar, 33. qux
console.log(toStringAll(data.slice(0,2)))
// 7. foo, 22. bar
console.log(toStringAll(data.slice(0,1)))
// 7. foo
console.log(toStringAll(data.slice(0,0)))
// Empty.
展开下面的sn-p,在浏览器中验证结果-
const empty =
{ id: 0, task: "" }
const task = (id = 0, task = "") =>
({ id, task })
const toString = (t = empty) =>
t === empty
? `Empty.`
: `${t.id}. ${t.task}`
const toStringAll = ([ t = empty, ...more ]) =>
more.length === 0
? toString(t)
: toString(t) + ", " + toStringAll(more)
const data =
[ task(7, "foo")
, task(22, "bar")
, task(33, "qux")
]
console.log(toStringAll(data))
// 7. foo, 22. bar, 33. qux
console.log(toStringAll(data.slice(0,2)))
// 7. foo, 22. bar
console.log(toStringAll(data.slice(0,1)))
// 7. foo
console.log(toStringAll(data.slice(0,0)))
// Empty.