1,实现柯里 curry
1)柯里化使用
const R = require('ramda');const func = R.curry((a, b) => [ a, b ]);console.log(func(1, 2)); // 直接使用函数,传两个参数,可以转为顺序的数组console.log(func(1)(2)); // 柯里化函数,传一部分参数,还会返回函数,再继续传参数console.log(func(R.__, 2)(1)); // ramda有一个__ 占位符,表示这个参数可以等下再传
2)实现柯里函数
// 自己修改书上的错误实现function _curryN(fn, args) {const length = fn.length;return (...arg) => {console.log('arg', arg);args = args.concat(arg);if (args.length === length) {return fn(...args);}return _curryN(fn, args);};}function curry(fn) {return _curryN(fn, []);}const func2 = curry((a, b, c) => [ a, b, c ]);// console.log(func2(1, 2, 3));console.log(func2(1)(2)(3));
注意:
- 访问函数的 .length 可以获取函数需要传递的参数个数
- 扩展符在做参数的时候,可以把之前的类数组转化成一个数组,但是在函数中的时候,args就表示这个转化以后的数组,如果再用扩展符去获取,就会导致args 变为原来的数值,而不是数组
- 所有参数传递完毕才调用fn。 ```javascript // 网上搜索答案 function _curryN(fn, args) { const length = fn.length; return function() { const newArgs = args.concat(Array.prototype.slice.call(arguments)); return newArgs.length < length ? _curryN.call(this, fn, newArgs) : fn.apply(this, newArgs); }; }
function curry(fn) { return _curryN(fn, []); }
const func2 = curry((a, b, c) => [ a, b, c ]);
console.log(func2(1, 2, 3)); console.log(func2(1)(2)(3));
注意:1. 函数的 arguments 属性的数据类型是对象1. Array.prototype.slice.call(arguments) 把类数组的参数对象转化为数组1. call 和 apply 的使用一样,都是把后边的参数传给前面的函数,但是需要使用 this 的作用域1. 如果需要使用到this的指向,就需要使用 function定义函数,而不能使用 箭头函数1. 如果递归函数第一层调用不需要参数,但是第二层调需要参数,可以在外面定义一个函数,传一个参数1. func2(1)(2)(3) 表示之前的函数返回一个函数,然后再继续调用,调用的循序是从右到左,既<br />3 -> 2 -> 1<a name="PnuXk"></a>## 2,函数式编程 —— compose情景:如果函数的嵌套比较多,a( b( c( d( e( f( g(1) ) ) ) ) ) ) 函数调用比较多层的情况,可以使用compose(题外话:其实Lisp语言中就存在函数的多层调用)<br />可以看到 上面函数的调用放向是 g -> f -> e -> d -> c -> b -> a 从右到左,compose的调用放向就是从右到左,同时ramda 还提供了 pipe 函数,可以从左到右调用1)ramda 的 compose 和 pipe 的使用```javascriptconst notInGlobal = R.not(check(global)); // R.not() 表示函数的结果取反const notInGlobal = R.compose(R.not, check(global)) // R.compose 从右到左const notInGlobal = R.pipe( check(global), R.not) // R.pipe 从左到右
2)实现compose函数
const compose = (...fns) => fns.reverse().reduce((a, b) => (...arg) => b(a(arg)));function compose(...funs) {if (funs.length === 0) {return arg => arg;}if (funs.length === 1) {return funs[0];}return funs.reverse().reduce((a, b) => function(...arg) {console.log('arg', arg); // 看arg 是什么return b(a(arg));});}function isTrue() {return false;}const fn2 = compose(R.not, isTrue);console.log('fn2 ---', fn2());console.log('fn2 ---', fn2(121)); // arg 是 fn2 传入的参数
注意:
- compose函数的重点就是传入的函数数组是从右向左执行,所以就是把参数 翻转,在堆叠执行
- (…fns) 在参数中使用箭头函数可以把传入的参数组合成一个数组为fns。
- Array.prototype.reduce( (a, b) => a + b) reduce传入的需要是一个函数,表示堆叠的加法器,举的例子表示把两个数相加,代码中 reduce( (a, b) => (…arg) => b(a(arg))) 表示把函数一层一层的调用
- arg 是最外层函数传入的参数
3,函数式编程基本概念
1,什么是函数式编程
面向对象编程和函数编程对比
// 面向对象编程var Flock = function(n) {this.seagulls = n;};Flock.prototype.conjoin = function(other) {this.seagulls += other.seagulls;return this;};Flock.prototype.breed = function(other) {this.seagulls = this.seagulls * other.seagulls;return this;};var flock_a = new Flock(4);var flock_b = new Flock(2);var flock_c = new Flock(0);var result = flock_a.conjoin(flock_c).breed(flock_b).conjoin(flo ck_a.breed(flock_b)).seagulls;
// 函数式编程var conjoin = function(flock_x, flock_y) {return flock_x + floc k_y};var breed = function(flock_x, flock_y) {return flock_x * flock_ y};var flock_a = 4;var flock_b = 2;var flock_c = 0;var result = conjoin(breed(flock_b,conjoin(flock_a, flock_c)),breed(flock_a, flock_b));
自己的理解:
1)函数里面不定义新的变量,所有的变量从参数中获取
2)让函数中的变量看起来更清晰
2,纯函数
纯函数也是需要,函数中所需要的参数从参数中传递过来,即使在一个函数中调用其他的两个函数,这两个函数的参数也是需要从主函数的参数中传递过来
// 不纯的var signUp = function(attrs) {var user = saveUser(attrs);welcomeUser(user);};var saveUser = function(attrs) {var user = Db.save(attrs);...};var welcomeUser = function(user) {Email(user, ...);...};// 纯的var signUp = function(Db, Email, attrs) {return function() {var user = saveUser(Db, attrs);welcomeUser(Email, user);};};var saveUser = function(Db, attrs) {...};var welcomeUser = function(Email, user) {...};
