函数式编程入门实践(一)(2)

怎么样?眼前一亮吧,没想到identity函数原来深藏不露,事实上虽然identity返回了原值,但是在这些函数中Javascript会对返回的值进行类型装换,变成了布尔值。比如filter函数。我们可以看MDN定义filter描述如下(看标粗的那一句)。

filter() calls a provided callback function once for each element in an array, and constructs a new array of all the values for which callback returns a value that coerces to true.

constant

const constant = v => () => v;

同样,这个函数...乍一看,也不知道具体有什么用。但是考虑场景如下:

onst p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('Hello!'); }, 200); }); p1.then(() => 'Hi').then(console.log); // Hi! p1.then(constant('Hi')).then(console.log); // Hi! p1.then('Hi').then(console.log); // Hello!

由于Promise.prototype.then只接受函数,如果我仅仅只需要传递一个值时,那么constant便会提供这种便利。当然这个并没有什么功能上的提升,但是的确提高了可阅读性,也是函数式编程的一个优点。

spreadArgs & gatherArgs

const spreadArgs = fn => argsArr => fn( ...argsArr ); const gatherArgs = fn => (...argsArr) => fn( argsArr );

嗯这两个函数见名知义。分别用于展开一个函数的所有参数和收集一个函数所有参数,这两个函数明显对立,那么它们的应用场景又是什么呢?

spreadArgs函数示例如下:

function cube(x, y, z) { return x * y * z; } function make(fn, points) { return fn(points); } console.log(make(cube, [3, 4, 5])); // NaN console.log(make(spreadArgs(cube), [3, 4, 5])); // 60

gatherArgs函数示例如下:

function combineFirstTwo([v1, v2]) { return v1 + v2; } console.log([1, 2, 3, 4, 5].reduce(combineFirstTwo)); // Uncaught TypeError console.log([1, 2, 3, 4, 5].reduce(gatherArgs(combineFirstTwo))); // 15

看完以上代码,简单的两个工具函数,轻易的做到了对一个函数的转换,从而使其适用于另一个场景。如果从此应该可以瞥见函数式编程的一点点魅力,那么下面的两个函数将给大家带来更多的惊喜。

partial & curry

const partial = (fn, ...presetArgs) => (...laterArgs) => fn(...presetArgs, ...laterArgs); const curry = (fn, arity = fn.length, nextCurried) => (nextCurried = prevArgs => nextArg => { const args = [...prevArgs, nextArg]; if (args.length >= arity) { return fn(...args); } else { return nextCurried(args); } })([]);

相信大家对函数柯里化应该或多或少有点了解。维基百科定义:

在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

当然得益于闭包的强大威力,柯里化这个武器得以诞生于Javascript世界。请大家先精读以上关于partiel、curry函数的代码。

喝一杯咖啡~

先模拟一个ajax函数如下:

function ajax(url, params, callback) { setTimeout(() => { callback( `GET ${url} \nparams: ${params} \ndata: Hello! ${params} ` ); }); }

考虑partial使用场景如下:

const fetchPerson = partial( ajax, "http://some.api/person" ); fetchPerson('Teddy Bear', console.log); /* GET params: Teddy Bear data: Hello! Teddy Bear */

考虑curry使用场景如下:

const fetchPerson = curry(ajax)('http://some.api/person'); const fetchUncleBarney = fetchPerson('Uncle Barney'); fetchUncleBarney(console.log); /* GET params: Uncle Barney data: Hello! Uncle Barney */

partial和curry函数功能相似,但又有具体的不同应用场景,但总体来说curry会比partial更自动化一点。
但是!相信看完示例的同学又会有一连串问号?为什么好好地参数不一次性传入,而非要分开多次传入这么麻烦?原因如下:

最首要的原因是partial和curry函数都允许我们通过参数控制将一个函数的调用在时间和空间上分开了。传统函数需要一次性将参数凑齐才能调用,但是有时候我们可以提前预置部分参数,在最终需要触发此函数时,才将剩余参数传入。这时候partial和curry就会变得十分有用。

partial和curry的存在让函数组合(compose)会更加便利。(函数组合也计划之后和大家分享,这里就不详细说了)。

当然最重要是也提升了可阅读性!一开始可能不这么以为,但是如果你实践操作感受之后,也许会改观。

P.S. 关于函数式编程的实践,大家可以使用lodash/fp模块进行入门实践。

一些思考
因为我也是函数式编程的初学者,如有不正确的地方,欢迎大家纠正~

接下来还是会继续整理FP的学习资料,学习实践,连载一些我对于函数式编程的学习与思考,希望和大家一起进步~

谢谢大家(●´∀`●)~

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/775cd1ce11fe5a0406b040ccbbbc7c4d.html