函数式编程学习

概念: 相同的输入,永远得到相同的输出,而且没有任何可观察的副作用

举个例子?:

let arr = [1, 2, 3];
arr.slice(0,3)  //=> 1,2,3
arr.splice(0,3) // => 1,2,3

虽然两个函数都返回了[1, 2, 3],但是splice的副作用是arr数组永久的发生了改变,在函数是编程中,我们需要的结果是稳定的,而不是把数据弄得一团糟的函数,这不是我们想要的

再举个例子?:

var minimum = 21;
var checkAge = age => {
    return age > minimum
}
// 上面是一个不纯的函数,下面是一个纯的函数

var checkAge = age => {
    var minimum = 21;
    return age > minimum
}

在第一个函数中,checkAge的结果取决于外部变量minimum的值,引入了外部的环境,从而增加了认知负荷

至此

我们来看看在概念中没有任何可观察的副作用有什么?

概念:副作用是在计算结果的过程中,系统状态的一种变化,或者与外部世界进行的可观察的交互

包含但不限于:

  1. 往数据库中插入记录
  2. 发送一个http请求
  3. 可变数据
  4. 访问系统状态
  5. .....

当然,并不是说要禁止使用一切副作用,如果函数是和外部事物打交道,那么这一点就无法保证了

回顾

函数的概念: 每一个输入值返回且只返回一个输出值

函数就是数学上的函数,而且是函数式编程的全部,总能根据相同的输入返回相同的输出,总能保证返回同一个结果

好处

函数式编程的好处:

  1. 可缓存性
  2. 可移植性(依赖明确,便于观察和理解
  3. 可测试性
  4. 引用透明性
  5. 并行代码(因为纯函数不需要访问共享的内存

柯里化

只传递给函数部分参数,让它返回一个函数来处理剩下的参数
let sayHello = msg => {
    return name => {
        return msg + ', My name: ' + name
    }
}

sayHello('Hello')('毅翔')

加上一个场景来一个柯里化的例子:

初始化一个年龄,再加上一个age去判断是否大于初始化这个年龄


let checkAge = initAge => {
    return age => {
        return age > initAge
    }
}
checkAge(22)(18) // fasle 判断 18 是否大于

Lodash库

在lodash库中使用curry

var _ = require('lodash')
// 判断age1 是否小于 age2
// 首先柯里化一个纯函数
let judge = _.curry((age1, age2) => {
    return age1 < age2
})
console.log(judge(1)(2))

// 判断数组arr中是否存在num
let include = _.curry((arr, num) => {
    return arr.includes(num)
})
console.log(include([1, 2, 3, 4])(2));

函数组合

let compose = (f, g) => {
    return (x) => {
        return f(g(x))
    }
}
let f = o => o.toUpperCase()
let g = o => o + '!'

compose(f, g)('asd')

最后

我理解的函数式编程是一种思维,是一种理想化的开发,了解函数式编程并不代表你一定会在项目中去使用,在JS开发中,可能会遇到dom操作,http请求,NodeJs中文件读写等等,会遇到很多副作用,参考某位作者,建议在代码中寻找平衡,最大发挥函数式编程的作用,当然也许,你并不需要它

参考

函数式编程GitBook
Noodles SegmentFault 技术周刊 Vol.16 - 浅入浅出 JavaScript 函数式编程


ilvseyinfu
25 声望5 粉丝

很帅,不会写代码