javascript函数式编程

为什么使用函数式编程

1. React、vue3都支持函数式编程
2. 函数式编程可以抛弃this
3. 打包中可更好利用tree taking过滤没有使用的代码
4. 方便单元测试,方便并行处理
5. 使代码更简洁,更通用,扩展性更好

什么是函数式编程

函数式编程(Functional Programming,FP)是编程范式之一s,与面向对象编程并列
函数式编程的思维方式:对运算过程对抽象
函数式编程的函数是数学中的函数即映射关系,如y=sin(x)
相同输入会得到相同输出(纯函数),如数组的slice方法
函数式编程是描述数据(函数)之间的映射

相关概念

1. 函数是一等公民

函数可存储到变量中,可作为函数的参数,可作为返回值

2. 高阶函数

概念:函数可作为参数传递给其他函数,函数可作为返回值
意义:可抽象具体的细节,而只关注输入与输出,高阶函数可抽象通用问题,更具有扩展性
常用的高阶函数:forEach, map, reduce, every, some, sort, filter, find等

3. 闭包

概念:函数和其周围的状态的引用捆绑在一起形成闭包,简而言之,在里层作用域中使用外层作用域拥有的变量,即形成闭包
本质:函数被放到调用栈执行后,会销毁此函数,但当其中的变量被外部引用时,不会被销毁和释放,故里层函数可使用外层函数的变量
例子:es6的let定义变量会形成闭包,例如:
for (let i = 0; i < 10; i++) {
	setTimeout(() => {
		console.log(i);
	}, 10)
}
此时打印值为1-9,但是如果使用var定义或let定义放到for循环的上方,则不能形成闭包,打印值就为10
4. 纯函数
概念:相同输入会得到相同输出,而且没有任何副作用,所谓副作用就是不可预测的结果,比如函数中使用服务端返回的数据,全局变量等
纯函数类似于数学中的计算公式,比如y=sin(x), x值只要确定,那么y的值就是定值
示例:数组的slice是纯函数,splice不是纯函数
函数式编程不会保留中间过程,只会有返回值,可以把返回值去当参数传给其他函数
好处:
1. 可缓存,因为相同输入有相同输出,所以可以把返回值缓存下来,不用参数相同时,去多次执行
2. 方便自我调试,对超出预期对结果可通过增加断言函数去打印结果
3. 并行处理,因为纯函数只依赖参数,而且不会修改全局变量,所以就算是多线程或多处都调用,也无须担心出现预期外的错误
副作用:会让纯函数变为不纯,比如配置文件,数据库,全局变量,dom
副作用会降低函数的通用性、扩展性、重用性,但副作用不可避免,比如dom交互等
5.柯里化
概念:当函数存在多个参数时,可调用函数时传递一部分参数,返回个新函数,再调用新函数时,传递剩下的参数,当参数全部传入后,再执行此函数
例子:
var _ = require("lodash");
var getSums = (a, b, c) => {return a + b + c};
var getSumsByCurry = _.curry(getSums);
getSumsByCurry(1)(2)(3); // 等价于getSums(1,2,3);
手写curry:
var curry = function (fn) {
    return function f(...args) {
        if (args.length >= fn.length ) {
            return fn(...args);
        }
        return function(...ab) {
            return f(...args, ...ab);
        }
    }
}
总结:柯里化可以得到缓存某些公共参数的函数,让函数的粒度更小,更灵活,可以把多个参数的函数变为只传一个参数的函数,方便函数组合时使用
6.函数组合
函数组合可以把多个细粒度的函数组合成新的函数,如果一个函数需要其他函数的返回值作为参数,这时就可以把这些函数组合成一个新的函数
函数式组合可最大限度的去重新利用细粒度的函数,提高函数的重复利用
手写函数组合,从右到左执行
function compose (...args) {
  return function (value) {
    return args.reverse().reduce(function (ret, fn) {
      return fn(ret)
    }, value)
  }
}
var fn = compose(a(), b());
fn(1) // 等价于a(b(1));
7.函子(functor)
概念:是一个特殊的容器,由对象实现,该对象必须有map方法,map方法可以对值进行处理
class Container {
  static of (value) {
    return new Container(value)
  }

  constructor (value) {
    this._value = value
  }

  map (fn) {
    return Container.of(fn(this._value))
  }
}
let r = Container.of(5)
          .map(x => x + 2)
          .map(x => x * x)

总结:
可处理函数对副作用,不在函数内调用副作用对函数,而是暴露出,比如对外暴露run方法,当执行run时,才处理某个传入的函数
map方法会调用传入对函数,返回值为处理过的新的函子
函子就是约定好有map特定作用函数的对象,把传入的value封装,当需要改变value时,通过map方法去实现
类型:
MayBe函子:兼容非空
Either函子:类似if else
IO函子:value为函数,当有需要时,由调用方手动调用
Pointed函子:实现了of方法当函子
Monad函子:使嵌套函子时,把调用方式拍扁
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值