[英]I need help understanding the rest and spread operator
这是代码:
const Pipe = (...fns) => fns.reduce((f,g) => (...args) => g(f(...args)));
那么通过(... fns)fns参数变成一个数组吧? 在这部分:
(f,g) => (...args)
args来自哪里? 是否有默认的args参数? 我看不懂这部分:
(...args) => g(f(...args))
我只是无法用这种嵌套包裹我的头,这里的减少是如此令人困惑。
你的第一个问题是你正在处理一个糟糕的pipe
实现 - 第二个问题是在新的JavaScript中有各种各样的扩展语法,并且并不总是清楚(对于初学者)哪一个被用于哪里
休息参数
rest参数收集数组中函数的提供参数。 这取代了JavaScript过去的旧arguments
对象
const f = (...xs) => xs console.log(f()) // [] console.log(f(1)) // [1] console.log(f(1,2)) // [1,2]
传播论点
spread参数允许您将数组(或任何可迭代的)作为参数传播给函数调用。 这取代了(几乎所有) Function.prototype.apply
实例
const g = (a,b,c) => a + b + c const args = [1,2,3] console.log(g(...args)) // 6
pipe
坏的原因
它不是一个完整的函数 - pipe
域是[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
重新实现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)) // 2
“但我听说递归很糟糕”
好的,所以如果要管理数千个函数,可能会遇到堆栈溢出。 在这种情况下,您可以像原始帖子一样使用stack-safe 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)) // 2
“我真的只是想了解我帖子中的代码”
好吧,让我们一步一步通过您的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))
那么当应用该函数时会发生什么呢? 让我们看看当我们使用一些参数Q
应用Pipe(a,b,c,d)
时
// 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
Pipe(a,b,c,d)(Q)
// evalutes to
d(c(b(a(Q))))
额外阅读
我已经就功能组合的主题做了很多写作。 我鼓励你探讨其中一些相关的问题/我已经就功能构成这个主题做了大量的写作。 我鼓励您探讨其中一些相关的问题/答案
如果有的话,你可能会在每个答案中看到compose
(或pipe
, flow
等)的不同实现。 也许其中一个会对你的良心说话!
那么@naomik打败了我几乎相同的答案,但我想我仍然会分享我必须帮助你解释这个功能如何以一种可能不那么神秘的方式运作。 (也许)
我想你已经知道“ ...
”是如何工作的(如果你没有,那么naomik的回答应该有助于此:D)
这是同一个管道函数的另一个版本,期望重写以更好地解释使用赋值进行的操作只是为了解释这一点。
Array.prototype.reduce所说的“减速”, toASingleFunction
,多次-一个呼吁在每个功能functionsToPipe
。 currentFunctionToPipe
首先是x => x + 1
然后是x => x * 2
,依此类推......
newFunction
的第一个值是theIdentityFunction
,reducer返回另一个函数nextNewFunction
。 顾名思义,它在下一次调用“reducer”( toASingleFunction
)时成为下一个newFunction
。
一旦减少了functionsToPipe
所有项目,最终的newFunction
将作为finalPipeFunction
返回。
/** * `x` goes to itself */ const theIdentityFunction = x => x; /** * the `reducer` that reduces the array of functions into a single function * using the value from the last function as the input to the next function */ const toASingleFunction = (newFunction, currentFunctionToPipe) => { const nextNewFunction = function(value) { // i'm not using arrow functions here just to explicitly show that `nextNewFunction` is, in fact, a function const valueFromLastFunction = newFunction(value); return currentFunctionToPipe(valueFromLastFunction); } return nextNewFunction; }; const pipe = (...functionsToPipe) => { const finalPipeFunction = functionsToPipe.reduce(toASingleFunction, /* start with */ theIdentityFunction); return finalPipeFunction; } const f = pipe( x => x + 1, x => x * 2, x => x * x ); console.log(f(2)) // ((2 + 1) * 2) ^ 2 === 36
也许这有帮助吗?
祝好运!
为了更好地理解,我已经将代码转换为块级别,如下所示,试图为有用的新手开发人员解释相关代码。 为了更好地实现@naomik有解决方案。
ES6
const Pipe = (...fns) => {
return fns.reduce((f, g) => {
return (...args) => {
return g(f(...args))
}
})
};
等效的ES5实现:
var Pipe = function () {
var fns = [];
for (var _i = 0; _i < arguments.length; _i++) {
fns[_i] = arguments[_i];
}
return fns.reduce(function (f, g) {
return function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return g(f.apply(void 0/*Undefined or can use 'null'*/, args));
};
});
};
逐个细分:(阅读代码注释以便更好地理解)
const inc = (num) => num+1 //Function to increment the number
const dbl = (num) => num*2 //Function double's the number
const sqr = (num) => num*num //Function Square's the number
/*Function breakdown*/
const _pipe = (f, g) => (...args) => g(f(...args)); //Second part
const Pipe = (...fns) => fns.reduce(_pipe); //First part
const incDblSqr = Pipe(inc, dbl, sqr) //Piping all the functions
const result = incDblSqr(2) // Piped function
console.log(result) // ((2+1)*2) ^ 2 = 36
阐释:
有问题的代码有助于将结果从一个函数传递到另一个函数。
上面代码的分步过程:
所有这些都是通过使用可以访问减少参数的闭包来实现的(为了清楚起见,请阅读下面链接的文章) 。
结论:有问题的代码有助于管理对前一个函数发出的结果进行操作的n个函数。
Andrew L. Van Slaars的最佳文章: https ://vanslaars.io/post/create-pipe-function/
为了清楚起见,请阅读上述文章以及@naomik解决方案
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.