阅读函数式编程(一)

函数式编程的定义

javascript 中, 函数被作为一等公民, 属于内置对象, 函数是对象中的一个子类型, 因此, 函数可以当作参数传递, 可以放到数组之中,本质上和普通的对象是一样的, 只不过相对于普通的对象而言, 函数可以被调用, 因此可以被称作可调用的对象。
在 js 中, 存在下面几种内置对象:
  1. String
  2. Number
  3. Boolean
  4. Object
  5. Function
  6. Array
  7. Date
  8. Regexp
  9. Error
这几种内置对象相当于语言中的类,可以通过使用 new 操作符进行调用。
使用函数式编程的目的在于:
我们希望去践行每一部分都能完美接合的理论,希望能以一种通用的、可组合的组 件来表示我们的特定问题,然后利用这些组件的特性来解决这些问题。

使用纯函数

什么是纯函数, 在介绍纯函数之前, 我们先看下关于纯函数的概念:
纯函数是这样一种函数,即相同的输入, 永远只能得到相同的输出,而且没有任何可以观察到的副作用。
对于有些函数而言,在函数执行的过程中发生了一些可能会影响外部状态的副作用, 包括下面几种:
  1. 外部状态发生了改变
  2. 读取文件
  3. console.log 数据
  4. 发送请求
总而言之, 在函数执行的过程中, 函数与外部环境发生了交互的过程,代表这个函数存在副作用, 存在副作用的不纯函数容易产生 bug, 数据状态难以追踪, 并且增加我们的认知负荷, 因此, 在函数编写的过程中要尽量较少函数执行的时候对于外部环境的影响。
这里对于纯函数而言, 相同的输入总会得到相同的输出, 这里类似于数学中的函数的概念, 在数学中的函数的概念中, 存在一个映射的改变:
函数只是两种数值之间的关系, 尽管相同的输入只能得到相同的输出, 但是不同的输入也可以得到相同的输出
因为在纯函数中,相同的输入只能得到相同的输出, 因此对于输入值是一定的情况下,对于输出的值则是一定的, 这样的情况下, 我们所要关心的只是这个函数做了什么,而不同尽力了解函数实现的具体细节, 这样能够增强我们调试代码的便利性。

使用纯函数的好处

  1. 可缓存性简单的一段代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    let squareNumber = memorize(function (x) return { x*x });
    squareNumber(4) => 16;
    squareNumber(4) => 16; // 从缓存中获取到的值

    // memorize 函数如下
    function memorize(f) {
    let cacheData = {};
    return function() {
    let arg = JSON.stringify(arguments);
    cacheData[arg] = cacheData[arg] || f.apply(f, arguments);
    return cacheData[arg];
    }
    }
    上面的这种纯函数的可缓存性类似于在 js 中的闭包的效果, 可以将值保存在函数内部。
  2. 依赖明确, 易于调试对于纯函数而言, 函数的依赖都是写在函数参数之中的, 更便于我们理解与调试, 对于纯函数而言, 与外部函数发生作用的仅仅是定义中传入的参数,函数内部则不会涉及到关于外部环境的变量。
  3. 引用透明性
    如果一段代码可以替换成它执行之后得到的结果, 而且是在不改变整个程序行为的情况下被替换的, 那么, 我们说这段函数具有引用透明性。
例如对于类似下面这类的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function isNewUser() {
// 判断是否是新用户
}
function loginIn(user) {
// 用户登录
}
function register(user) {
// 注册新用户
}
function start(user) {
if (isNewUser(user)) {
register(user);
return;
}
loginIn(user);
}
在上面的代码中, 三个行为, 判断是否是新用户, 登录, 注册, 三个部分的逻辑封装在三个函数中, 从而更容易理解。