gpb*_*lio 2 javascript ecmascript-6
这是代码:
const Pipe = (...fns) => fns.reduce((f,g) => (...args) => g(f(...args)));
Run Code Online (Sandbox Code Playgroud)
那么通过(... fns)fns参数变成一个数组吧?在这部分:
(f,g) => (...args)
Run Code Online (Sandbox Code Playgroud)
args来自哪里?是否有默认的args参数?我看不懂这部分:
(...args) => g(f(...args))
Run Code Online (Sandbox Code Playgroud)
我只是无法用这种嵌套包裹我的头,这里的减少是如此令人困惑.
你的第一个问题是你正在处理一个糟糕的实现pipe- 第二个问题是在新的JavaScript中有各种各样的扩展语法,并且(对于初学者)并不总是清楚哪一个被用于哪里
休息参数
rest参数收集数组中函数的提供参数.这取代了argumentsJavaScript过去的旧对象
const f = (...xs) =>
xs
console.log(f()) // []
console.log(f(1)) // [1]
console.log(f(1,2)) // [1,2]Run Code Online (Sandbox Code Playgroud)
传播论点
spread参数允许您将数组(或任何可迭代的)作为参数传播给函数调用.这取代了(几乎所有)实例Function.prototype.apply
const g = (a,b,c) =>
a + b + c
const args = [1,2,3]
console.log(g(...args)) // 6Run Code Online (Sandbox Code Playgroud)
为什么那pipe很糟糕
它不是一个完整的函数pipe--s域是[Function](函数数组),但如果使用一个空的函数数组,这个实现将产生一个错误(TypeError: Reduce of empty array with no initial value)
可能不会立即明白这将如何发生,但它可能以各种方式出现.最值得注意的是,当要应用的函数列表是在程序中的其他位置创建并最终为空的数组时,Pipe灾难性地失败
const foo = Pipe()
foo(1)
// TypeError: Reduce of empty array with no initial value
const funcs = []
Pipe(...funcs) (1)
// TypeError: Reduce of empty array with no initial value
Pipe.apply(null, funcs) (1)
// TypeError: Reduce of empty array with no initial value
Pipe.call(null) (1)
// TypeError: Reduce of empty array with no initial value
Run Code Online (Sandbox Code Playgroud)
重新实现 pipe
这是无数的实现之一,但它应该更容易理解.我们使用了rest参数,并使用了spread参数.最重要的是,pipe 始终返回一个函数
const pipe = (f,...fs) => x =>
f === undefined ? x : pipe(...fs) (f(x))
const foo = pipe(
x => x + 1,
x => x * 2,
x => x * x,
console.log
)
foo(0) // 4
foo(1) // 16
foo(2) // 36
// empty pipe is ok
const bar = pipe()
console.log(bar(2)) // 2Run Code Online (Sandbox Code Playgroud)
"但我听说递归很糟糕"
好的,所以如果要管理数千个函数,可能会遇到堆栈溢出.在这种情况下,您可以使用原始帖子中的堆栈安全Array.prototype.reduce(或reduceRight).
这一次pipe,我将把问题分解成更小的部分,而不是做任何事情.每个部件都有不同的用途,pipe现在只关注部件如何组合在一起.
const comp = (f,g) => x =>
f(g(x))
const identity = x =>
x
const pipe = (...fs) =>
fs.reduceRight(comp, identity)
const foo = pipe(
x => x + 1,
x => x * 2,
x => x * x,
console.log
)
foo(0) // 4
foo(1) // 16
foo(2) // 36
// empty pipe is ok
const bar = pipe()
console.log(bar(2)) // 2Run Code Online (Sandbox Code Playgroud)
"我真的只是想了解我帖子中的代码"
好的,让我们一步一步通过你的pipe功能,看看发生了什么.因为reduce会多次调用reduce函数,所以我args每次都会使用一个唯一的重命名
// given
const Pipe = (...fns) => fns.reduce((f,g) => (...args) => g(f(...args)));
// evaluate
Pipe(a,b,c,d)
// environment:
fns = [a,b,c,d]
// reduce iteration 1 (renamed `args` to `x`)
(...x) => b(a(...x))
// reduce iteration 2 (renamed `args` to `y`)
(...y) => c((...x) => b(a(...x))(...y))
// reduce iteration 3 (renamed `args` to `z`)
(...z) => d((...y) => c((...x) => b(a(...x))(...y))(...z))Run Code Online (Sandbox Code Playgroud)
那么当应用该函数时会发生什么呢?让我们看一下当我们应用Pipe(a,b,c,d)一些参数的结果时Q
// return value of Pipe(a,b,c,d) applied to `Q`
(...z) => d((...y) => c((...x) => b(a(...x))(...y))(...z)) (Q)
// substitute ...z for [Q]
d((...y) => c((...x) => b(a(...x))(...y))(...[Q]))
// spread [Q]
d((...y) => c((...x) => b(a(...x))(...y))(Q))
// substitute ...y for [Q]
d(c((...x) => b(a(...x))(...[Q]))
// spread [Q]
d(c((...x) => b(a(...x))(Q))
// substitute ...x for [Q]
d(c(b(a(...[Q])))
// spread [Q]
d(c(b(a(Q)))Run Code Online (Sandbox Code Playgroud)
就像我们预期的那样
// run
Pipe(a,b,c,d)(Q)
// evalutes to
d(c(b(a(Q))))
Run Code Online (Sandbox Code Playgroud)
额外阅读
我已经就功能组合的主题做了很多写作.我鼓励你探讨一些相关的问题/我已经就功能构成这个主题做了大量的写作.我鼓励您探讨其中一些相关的问题/答案
如果有的话,你可能会看到一个不同的实现的compose(或者pipe,flow在每个答案等).也许其中一个会对你的良心说话!
| 归档时间: |
|
| 查看次数: |
494 次 |
| 最近记录: |