1. 避免包裹函数
// 这行
ajaxCall(json => callback(json));
// 等价于这行
ajaxCall(callback);
// 那么,重构下 getServerStuff
const getServerStuff = callback => ajaxCall(callback);
// ...就等于
const getServerStuff = ajaxCall // <-- 看,没有括号哦
如果一个函数被不必要地包裹起来了,而且发生了改动,那么包裹它的那个函数也要做相应的变更
httpGet('/post/2', json => renderPost(json));
如果 httpGet 要改成可以抛出一个可能出现的 err 异常,那我们还要回过头去把“胶水”函数也改了。
// 把整个应用里的所有 httpGet 调用都改成这样,可以传递 err 参数。
httpGet('/post/2', (json, err) => renderPost(json, err));
写成一等公民函数的形式,要做的改动将会少得多
httpGet('/post/2', renderPost); // renderPost 将会在 httpGet 中调用,想要多少参数都行
后一个就显得更加通用,可重用性也更高
// 只针对当前的博客
const validArticles = articles =>
articles.filter(article => article !== null && article !== undefined),
// 对未来的项目更友好
const compact = xs => xs.filter(x => x !== null && x !== undefined);
2. 纯函数的好处
纯函数是这样一种函数,即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用。
var xs = [1,2,3,4,5];
// 纯的
xs.slice(0,3); //=> [1,2,3]
xs.slice(0,3); //=> [1,2,3]
xs.slice(0,3); //=> [1,2,3]
// 不纯的
xs.splice(0,3); //=> [1,2,3]
xs.splice(0,3); //=> [4,5]
xs.splice(0,3); //=> []
副作用可能包括…
副作用是在计算结果的过程中,系统状态的一种变化,或者与外部世界进行的可观察的交互
副作用可能包含,但不限于:
- 更改文件系统
- 往数据库插入记录
- 发送一个 http 请求
- 可变数据
- 打印/log
- 获取用户输入
- DOM 查询
- 访问系统状态
追求“纯”的理由
可缓存性(Cacheable)
// 下面的代码是一个简单的实现,尽管它不太健壮。
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];
};
};
值得注意的一点是,可以通过延迟执行的方式把不纯的函数转换为纯函数:它总是会根据相同的输入返回相同的输出:给定了 url
和 params
之后,它就只会返回同一个发送 http 请求的函数。
var pureHttpCall = memoize(function(url, params){
return function() { return $.getJSON(url, params); }
});
可测试性(Testable)
合理性(Reasonable)
很多人相信使用纯函数最大的好处是引用透明性(referential transparency)。如果一段代码可以替换成它执行所得的结果,而且是在不改变整个程序行为的前提下替换的,那么我们就说这段代码是引用透明的