组合函数看起来像是在搭积木。你就是一个孩子,能够随意选择两个积木(函数),让它们拼接(结合)一下,拼接成一个新的玩具(函数)。组合的用法以下:html
var compose = function(f,g) {
return function(x) {
return f(g(x));
};
};
复制代码
f
和g
都是函数,x
是在它们之间经过“管道”传输的值。这里得注意一下 compose 函数是组合代码思想中最重要的一环,下面👇会常常用到。你们能够提早 mark 一下git
var toUpperCase = function(x) { return x.toUpperCase(); };
var exclaim = function(x) { return x + '!'; };
var shout = compose(exclaim, toUpperCase);
shout("send in the clowns");
//=> "SEND IN THE CLOWNS!"
复制代码
就像这样咱们把两个函数组合以后返回一个新的函数。编程
思考一下: 组合的函数和一个完整流程的函数有什么区别 数组
var shout = function(x){
return exclaim(toUpperCase(x));
};
复制代码
能够看到组合的函数就像乐高玩具同样能够自由的组合成其余的可用的完整的模型乐高建模(完整功能的函数),可是一个像上方同样完整功能,不可拆卸对的函数的。它就像一个已经完成的手办。架构
让代码从右向左运行,而不是由内而外运行,我以为能够称之为“左派”(消音~)。咱们来看一个顺序很重要的例子:app
var curry = require("lodash").curry;
var reduce = curry(function(f, init, arr){
return arr.reduce(f, init);
});
// 感谢掘友(@thsing772)提示,这里勘误一下 reduce 函数,应该是须要先 curry 处理一下,才能以下使用。
var head = function(x) { return x[0]; };
var reverse = reduce(function(acc, x){ return [x].concat(acc); }, []);
// 固然你也能够不使用 curry
// var reverse = x => x.reduce(function (arr,x){return [x].concat(ar)}[]);
var last = compose(head, reverse);
last(['jumpkick', 'roundhouse', 'uppercut']);
//=> 'uppercut'
复制代码
上面就是一个反转数组的操做,这里咱们看到一个组合函数的执行顺利,咱们能够主动的操做函数进行从左到右的方式,可是从右向左的执行顺序更加符合数学上的含义。基本的高中数学知识ide
// 结合律(associativity)
var associative = compose(f, compose(g, h)) == compose(compose(f, g), h);
// true
compose(toUpperCase, compose(head, reverse));
// 或者
compose(compose(toUpperCase, head), reverse);
复制代码
结合律的好处是任何一个函数分组均可以被拆解开来,而后再以他们本身的组合打包在一块儿,组合成新的函数。curry 就是咱们的工具包。函数式编程
var loudLastUpper = compose(exclaim, toUpperCase, head, reverse);
// 或
var last = compose(head, reverse);
var loudLastUpper = compose(exclaim, toUpperCase, last);
// 或
var last = compose(head, reverse);
var angry = compose(exclaim, toUpperCase);
var loudLastUpper = compose(angry, last);
// 更多变种...
复制代码
pointfree 模式是,no data 模式的意思。这里有一句来自《Love Story》70 年代的电影的台词--“Love means never having to say you're sorry”。函数
// 非 pointfree,由于提到了数据:word
var snakeCase = function (word) {
return word.toLowerCase().replace(/\s+/ig, '_');
};
// pointfree
var snakeCase = compose(replace(/\s+/ig, '_'), toLowerCase);
// 不明白为何看看最上面的 compose 函数,而后在 控制台试试 snakeCase 函数
snakeCase('Hello World')
// hello_world
复制代码
在 pointfree 版本中,不须要 word 参数就能构造函数;而在非 pointfree 的版本中,必需要有 word 才能进行一切操做。工具
pointfree 模式可以帮助咱们减小没必要要的命名,让代码保持简洁和通用。
组合的一个常见错误是,在没有局部调用以前,就组合相似 map 这样接受两个参数的函数。
// 下面部分函数来自于上面的 #### 结合律的好处
// 错误作法:咱们传给了 `angry` 一个数组,根本不知道最后传给 `map` 的是什么东西。
var latin = compose(map, angry, reverse);
latin(["frog", "eyes"]);
// error
// 正确作法:每一个函数都接受一个实际参数。
var latin = compose(map(angry), reverse);
latin(["frog", "eyes"]);
// ["EYES!", "FROG!"])
复制代码
使用 trace(追踪) 来跟踪你的函数
var trace = curry(function(tag, x){
console.log(tag, x);
return x;
});
var dasherize = compose(join('-'), toLower, split(' '), replace(/\s{2,}/ig, ' '));
dasherize('The world is a vampire');
// TypeError: Cannot read property 'apply' of undefined
--------------
// 看到报错了,来 trace 一下
var dasherize = compose(join('-'), toLower, trace("after split"), split(' '), replace(/\s{2,}/ig, ' '));
// after split [ 'The', 'world', 'is', 'a', 'vampire' ]
-------------
// tolower 的参数徐亚的是一个数组,因此这里咱们 fix 一下咱们的代码
var dasherize = compose(join('-'), map(toLower), split(' '), replace(/\s{2,}/ig, ' '));
dasherize('The world is a vampire');
// 'the-world-is-a-vampire'
复制代码
trace 的好处就是能够直接定位到函数调用的地方,容许咱们在某个特定的点去观察咱们的函数数据
咱们能够认为哦组合是高于其余全部原则的设计原则,这是由于组合让咱们的代码简单而富有可读性。另外范畴学将在应用架构、模拟反作用和保证正确性方面扮演重要角色。
name | demo |
---|---|
CamelCase(小驼峰) | personId |
PascalCase(帕斯卡/大驼峰) | PersonId |
SnakeCase(下横线) | person_id |
KebabCase(中横线) | person-id |