什么是pointfree
pointfree是指函数无须提及将要操作的数据是什么样的。
一等公民的函数、柯里化(curry)以及函数组合(compose)协作起来非常有助于实现这种模式。
compose实现(具体请看js函数式编程之代码组合):
function compose(){
var args=arguments;
var start=args.length - 1;
return function(){
var i = start;
var result=args[i].apply(this,arguments);
while(i--){
result= args[i].call(this,result);
}
return result;
}
}
curry函数实现(具体请看js函数式编程之柯里化):
var curry = function(fn, args) {
var length = fn.length, // 获取形参个数
args = args || [];
return function() {
var newArgs = args.slice(0).concat([].slice.call(arguments));
// 判断实参参数个数是否大于或等于形参个数
if(newArgs.length >= length) {
return fn.apply(this, newArgs);
}else {
return curry.call(this, fn, newArgs);
}
}
}
第一个例子:
// 非 pointfree,因为提到了数据:word
var snakeCase = function (word) {
return word.toLowerCase().replace(/\s+/ig, '_');
};
// pointfree
var snakeCase = compose(replace(/\s+/ig, '_'), toLowerCase);
看到 replace
是如何被局部调用的了么?这里所做的事情就是通过管道把数据在接受单个参数的函数间传递。利用 curry,我们能够做到让每个函数都先接收数据,然后操作数据,最后再把数据传递到下一个函数那里去。另外注意在 pointfree 版本中,不需要 word
参数就能构造函数;而在非 pointfree 的版本中,必须要有 word
才能进行一切操作。
第二个例子:
// 非 pointfree,因为提到了数据:name
var initials = function (name) {
return name.split(' ').map(compose(toUpperCase, head)).join('. ');
};
var join = curry(function(separator, arr) { return arr.join(separator)});
var map = curry(function(fn, arr) { return arr.map(fn)});
var split = curry(function(separator, str) { return str.split(separator)});
var toUpperCase = function(x) { return x.toUpperCase(); };
var head = function(x) { return x[0]; };
// pointfree
var initials = compose(join('. '), map(compose(toUpperCase, head)), split(' '));
initials("hunter stockton thompson"); // 'H. S. T'
pointfree本质就是使用一些通用的函数,组合出各种复杂运算,上层运算不需要直接操作数据,而是通过底层函数去处理,即不适用所要处理的值,只合成运算过程。
pointfree好处
pointfree 模式能够帮助我们减少不必要的命名,让代码保持简洁和通用。
相关系列文章:
js函数式编程之偏应用函数 (Partial Application)
参考资料: JS 函数式编程指南