函数式编程在耳边回响了多年,今天就来详细了解一下它吧。
函数式编程的主要特征是:函数是一等公民。它建议大家写纯函数、没有副作用的函数。
讨论完纯函数的内容,我们会看一下最重要的应用:函数的柯里化。
纯函数是这样一种函数,即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用。其中的副作用指的是:跟函数外部环境发生的交互。包括但不限于:
- 更改文件系统
- 往数据库插入记录
- 发送一个 http 请求
- 可变数据
- 打印/log
- 获取用户输入
- DOM 查询
- 访问系统状态
例如:slice
是纯函数而splice
不是。
var memoize = function(f) { var cache = {}; return function() { var arg_str = JSON.stringify(arguments); cache[arg_str] = cache[arg_str] || f.apply(f, arguments); return cache[arg_str]; }; }; var squareNumber = memoize(function(x){ return x*x; }); squareNumber(4); //=> 16 squareNumber(4); // 从缓存中读取输入值为 4 的结果 //=> 16
值得注意的一点是,可以通过延迟执行的方式把不纯的函数转换为纯函数:
var pureHttpCall = memoize(function(url, params){ return function() { return $.getJSON(url, params); } });
纯函数对于依赖很诚实,这样就能知道它的目的。可以说是把所有需要的(可变的)参数都传递进来。
简单地给函数一个输入,就能有输出了。
如果一段代码可以替换成它执行所得的结果,而且是在不改变整个程序行为的前提下替换的,那么我们就说这段代码是引用透明的。这样会对理解代码非常重要,因为“可以使用等式推导”(equational reasoning)的技术来分析代码,就是“一对一”替换的情况下手动执行代码。
最后一点,也是决定性的一点:我们可以并行运行任意纯函数。因为纯函数根本不需要访问共享的内存,而且根据其定义,纯函数也不会因副作用而进入竞争态(race condition)。
并行代码在服务端 js 环境以及使用了 web worker 的浏览器那里是非常容易实现的,因为它们使用了线程(thread)。不过出于对非纯函数复杂度的考虑,当前主流观点还是避免使用这种并行。
curry 的概念很简单:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
function curry(fn) { return function finalCurry(...args) { if (args.length >= fn.length) { return fn(...args); } else { return function (...args2) { return finalCurry.apply(null, args.concat(args2)); } } } }
文章来源: 什么是函数式编程