函数式编程主要描述的是数据(函数)之间的映射。
函数是一等公民
- 函数可以存储在变量中
- 函数可以作为参数
- 函数可以作为返回值
高阶函数
- 函数可以作为参数
// 模拟封装一个filter,获取一个数组中所有的偶数
function filter (arr, fn) {
let results = [];
for (val of arr) {
if (fn(val)) results.push(val);
}
return results;
}
let a = [1, 2, 3, 4];
let b = filter(a, v => !(v % 2));
console.log(b);
- 函数可以作为返回值
// 模拟once函数
function once (fn) {
let status = true;
return function () {
if (status) {
fn.apply(fn, arguments);
status = false;
}
}
}
function pay (money) {
console.log(`支付了 ${money}元`);
}
let a = once(pay);
a(2);
a(2);
a(2);
a(2);
使用高阶函数的意义:抽象函数,屏蔽细节,关注目标
- 闭包
函数在执行的时候会放到一个执行栈上当函数执行完毕之后会从执行栈上移除,但是堆上的作用域成员因为被外部引用不能被释放,因此内部函数依然可以访问外部的成员,如上面的封装的once - 纯函数
相同的输入永远会得到相同的输出-
特点:
- 函数式编程不会保留计算中间的结果,所以变量是不可变的(无状态的)
- 可以把一个函数的执行结果交给另一个函数去处理好处
-
好处
- 可缓存,因为相同的输入时钟会有相同的输出,所以可以把纯函数的执行结果缓存起来
// 模拟lodash中的memoize函数 function memoize (fn) { let cache = {}; return function () { let arg_str = JSON.stringify(arguments); cache[arg_str] = cache[arg_str] || fn.apply(fn, arguments); return cache[arg_str]; } } let c = memoize(function (a, b) { console.log('执行函数'); return a + b; }); console.log(c(2, 3)); console.log(c(2, 3)); console.log(c(2, 3));
上面的例子中,只有第一次会打印出‘执行函数’,之后只会打印出函数运行结果,说明memoize缓存了函数执行结果
- 多线程并行操作下可以避免出现意外情况
-
副作用
如果一个函数需要依赖外部状态就无法保证输出相同,从而让函数变得不纯,就会带来副作用
-
柯里化
解决上面纯函数中的硬编码的问题
当一个函数有多个参数的时候,先传递一部分参数调用它(这部分参数是之后不会变的,基本上市固定参数),然后返回一个新的函数接收剩余的参数,返回结果
// 模拟lodash中的curry函数
function curry (fn) {
return function curriedFn (...args) {
// 判断函数的形参是否小于实参
if (fn.length > args.length) {
return function () {
return curriedFn(...args.concat(Array.from(arguments)));
}
}
// 函数的形参等于实参
return fn(...args);
}
}
function add (a, b, c) {
return a + b + c;
}
console.log(curry(add)(1)(2, 3));
console.log(curry(add)(1, 3)(2));
console.log(curry(add)(1)(2)(3));
总结:
柯里化其实就是给一个多元函数传递较少的参数而得到一个已经记住某些固定参数的新函数,让函数更灵活,粒度更小
函数组合
函数组合可以把细粒度的函数重新组合成一个新的函数
// 模拟实现lodash中的flowRight方法
function compose (...args) {
return function (value) {
return args.reverse().reduce((pre, cur) => {
return cur(pre);
}, value)
}
}
// hello world => HELLO-WORLD
function split (str) {
return str.split(' ');
}
function firstToUpper (arr) {
return arr.map(v => v.toUpperCase());
}
function join (arr) {
return arr.join(' ')
}
let a = compose(join, firstToUpper, split);
console.log(a('hello world'));
其实上述代码就是Point Free模式,
Point Free: 1、不需要指明处理的数据 2、只需要合成运算过程 3、需要定义一些辅助的基本运算函数
const f = fp.flowRight(fp.join('-'), fp.map(_toLower), fp.split(' '));
以上就是学过函数式编程(部分)之后的总结,受益匪浅,期待之后的笔记鸭~~~