js 函数式编程
函数式的思想, 就是不断地用已有函数, 来组合出新的函数。
函数式编程具有五个鲜明的特点:
1. 函数是"第一等公民"
指的是函数与其他数据类型一样,处于平等地位
2. 只用"表达式",不用"语句"
"表达式"(expression)是一个单纯的运算过程,总是有返回值;
"语句"(statement)是执行某种操作,没有返回值。
3. 没有"副作用"
指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),
产生运算以外的其他结果。
4. 不修改状态
变量往往用来保存"状态"(state)。不修改变量,意味着状态不能保存在变量中,
函数式编程使用参数保存状态
5. 引用透明
指的是函数的运行不依赖于外部变量或"状态",只依赖于输入的参数,任何时候只要参数相同,
引用函数所得到的返回值总是相同的
函数式编程的意义:
1. 代码简洁,开发快速
2. 接近自然语言,易于理解
3. 更方便的代码管理
4. 易于"并发编程"
5. 代码的热升级
---------------------分割线------------------------
在 JavaScript 中,函数本身为一种特殊对象,属于顶层对象,
不依赖于任何其他的对象而存在,因此可以将函数作为传出 / 传入参数,
可以存储在变量中,可以做一切其他对象可以做的事情。
自调用函数(递归--自己调用自己)实际上是高阶函数的一种形式。
函数式编程示例:
-
// 阶乘的一般实现
-
function factorial(n) {
-
if (n ==
1) {
-
return
1;
-
}
else {
-
return factorial(n -
1) * n;
-
}
-
}
-
-
// 阶乘的函数式编程风格实现
-
function mul(a, b){
return a*b; }
-
function dec(x){
return x -
1; }
-
function equal(a, b){
return a==b; }
-
-
function factorial(n) {
-
if (equal(n,
1)) {
-
return
1;
-
}
else {
-
return mul(n, factorial(dec(n)));
-
}
-
}
---------------------分割线------------------------
函数柯里化:
是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,
并且返回接受余下的参数而且返回结果的新函数的技术。
我的理解就是需要反复调用一个方法a(),它的参数中有一个参数在一定的状况下的值不易改变,
每次调用都写很多次未免太过麻烦,而且也体现不出来它描述和解决的一类问题,这种情况下,
为了避免这两个问题,我们就将其中那个不容易改变的参数给固定下来,新生成一个函数来应对
这一类问题。
加法柯里化的简单实现:
-
//加法
-
function adder(num) {
-
return
function(x) {
-
return num + x;
-
}
-
}
-
-
var add5 = adder(
5);
-
var add6 = adder(
6);
-
-
console.log(add5(
1));
// 6
-
console.log(add6(
1));
// 7
这里的add5表示的函数可以解决基数为5的加法问题,只需要传递一个参数就可以了,
而不必像普通的加法函数那样每次调用都必须添加两个参数。
-
//计算m的n次方
-
var powerOfN =
function(n){
-
return
function(m){
-
var res =
1;
-
for(
var i =
0; i < n; ++i){
-
res *= m;
-
}
-
return res;
-
} ;
-
};
-
//按需生成
-
var powerOf2 = powerOfN(
2);
-
var powerOf3 = powerOfN(
3);
-
//调用传参
-
console.log(powerOf2(
3));
-
console.log(powerOf3(
2));
柯里化通用实现:
-
function curry(fn) {
-
var args = [].slice.call(
arguments,
1);
-
return
function() {
-
var inargs = [].slice.call(
arguments);
-
console.log(args.concat(inargs));
-
return fn.apply(
null, args.concat(inargs));
-
}
-
}
-
-
function curry(fn) {
-
var args = [].slice.call(
arguments,
1);
-
return
function() {
-
var inargs = [].slice.call(
arguments);
-
console.log(args.concat(inargs));
-
return fn.apply(
null, args.concat(inargs));
-
}
-
}
-
function add(num1, num2) {
-
return num1 + num2;
-
}
-
var newAdd = curry(add,
5);
-
console.log(newAdd(
6));
柯里化将降低了函数使用的普遍性,增加了使用的特异性,使用柯里化需要认真识别
其使用的场景,在符合要求的地方使用,不然会显得啰嗦,降低代码的可读性。
---------------------分割线------------------------
高阶函数
高阶函数即为对函数的进一步抽象,就是以其它函数为输入,或者返回一个函数为输出的函数。
高阶函数最常见的应用如 map(映射), reduce(规约), forEach(遍历), filter(过滤)等,
它们都是以传入不同的函数来以不同的方式操作数组元。
简单应用:
-
function foo(f, g) {
-
return
function() {
-
return f.call(
null, g.apply(
null,
arguments));
-
//return f(g.apply(null, arguments)); 也是可以的
-
}
-
}
-
var sum =
function(x, y) {
-
return x + y;
-
}
-
var square =
function(x) {
-
return x * x;
-
}
-
var squareofsum = foo(square, sum);
-
squareofsum(
2,
3);
// 25
下面我们来看一下怎样从过程式编程过渡到函数式编程的:
1>形式一
-
var sum =
function(x, y) {
-
return x + y;
-
}
-
var square =
function(x) {
-
return x * x;
-
}
-
function foo(){
-
return square( sum(
2,
3) );
-
}
-
foo();
// 25
2>形式二
-
var sum =
function(x, y) {
-
return x + y;
-
}
-
var square =
function(x) {
-
return x * x;
-
}
-
function foo(f, g){
-
return f( g(
2,
3) );
-
}
-
foo(square, sum);
// 25
3>形式三
-
var sum =
function(x, y) {
-
return x + y;
-
}
-
var square =
function(x) {
-
return x * x;
-
}
-
function foo(f, g){
-
return
function(){
-
var num1 =
arguments[
0];
-
var num2 =
arguments[
1];
-
console.log(num1, num2);
// 2 3
-
var temp = g(num1, num2);
-
console.log(temp);
// 5
-
return f(temp);
-
};
-
}
-
var myfunc = foo(square, sum);
-
myfunc(
2,
3);
// 25
4>形式四
-
var sum =
function(x, y) {
-
return x + y;
-
}
-
var square =
function(x) {
-
return x * x;
-
}
-
function foo(f, g){
-
return
function(){
-
var temp = g.apply(
null,
arguments);
-
return f(temp);
-
};
-
}
-
var myfunc = foo(square, sum);
-
myfunc(
2,
3);
// 25
最后的形式四就是我们想要得到的效果。
---------------------分割线------------------------
其他示例:
1>
-
function foo(fn, array, value){
-
var res = array.map(
function(ele){
-
return fn.apply(
null, [ele].concat(value));
-
});
-
return res;
-
}
-
-
function add(x, y) {
-
return x + y;
-
}
-
-
function sub(x, y) {
-
return x - y;
-
}
-
-
console.log( foo(add, [
1,
2,
3],
3) );
// [4, 5, 6]
-
console.log( foo(sub, [
1,
2,
3],
3) );
// [-2, -1, 0]
2>
-
function multicast(fn) {
-
return
function (){
-
var pre =
arguments[
0];
-
var rest =
arguments[
1];
-
var ret = pre.map(
function(ele) {
-
return fn.apply(
this, [ele].concat(rest));
-
});
-
return ret;
-
}
-
}
-
-
function add(x, y) {
-
return x + y;
-
}
-
var newAdd = multicast(add);
-
console.log(newAdd([
1,
2,
3],
3));
// [4, 5, 6]
-
-
function sub(x, y) {
-
return x - y;
-
}
-
var newSub = multicast(sub);
-
console.log(newSub([
1,
2,
3],
3));
// [-2, -1, 0]
参考资料:
https://blog.oyanglul.us/javascript/functional-javascript.html
http://www.phodal.com/blog/javascript-higher-order-functions/
http://www.cnblogs.com/wing011203/archive/2013/07/07/3176641.html
http://www.ibm.com/developerworks/cn/web/1006_qiujt_jsfunctional/
http://www.cnblogs.com/pigtail/p/3447660.html
http://www.ruanyifeng.com/blog/2012/04/functional_programming.html