纯函数

纯函数的概念

  • 纯函数: 相同的输入始终会得到相同的输出,而且没有任何可观察的副作用

    • 纯函数就类似数学中的的函数(用来描述输入和输出之间的关系),y=f(x)

image-20221018205415808

  • Lodash 是一个纯函数的功能库,提供了对数组,数字,对象,字符串,函数等操作的一些方法
  • 数组的slice和splice分别是纯函数和不纯的函数
    • slice 返回数组中的指定部分,不会改变原数组
    • splice 对数组进行操作返回该数组,会改变原数组
  • 函数式编程不会保留计算中的结果,所以变量是不可变的(无状态)
  • 我们可以把一个函数的执行结果交给另一个函数去处理

多次调用 slice 发现相同的输入得到相同的输出,所以是纯函数, splice 多次调用之后相同的输入输出结果不一致,splice 改变了原数组,所以splice不是纯函数

let array = [1, 2, 3, 4, 5,]

// 纯函数
console.log(array.slice(0, 3))// 1, 2, 3
console.log(array.slice(0, 3))// 1, 2, 3
console.log(array.slice(0, 3))// 1, 2, 3

// 不纯的函数
console.log(array.splice(0, 3))// 1, 2, 3
console.log(array.splice(0, 3))// 4, 5
console.log(array.splice(0, 3))// []

写一个纯函数

let add = function (n1, n2) {
  return n1 + n2
}

console.log(add(1, 2))// 3
console.log(add(1, 2))// 3
console.log(add(1, 2))// 3
console.log(add(1, 2))// 3

Lodash 介绍

Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库。Lodash 中提供了很多方法

  • chunk :将数组(array)拆分成多个 size 长度的区块,并将这些区块组成一个新数组。 如果array 无法被分割成全部等长的区块,那么最后剩余的元素将组成一个区块。
    • 参数1:array 需要处理的数组
    • 参数2: size 每个分块的长度
_.chunk(['a', 'b', 'c', 'd'], 2);
// => [['a', 'b'], ['c', 'd']]

_.chunk(['a', 'b', 'c', 'd'], 3);
// => [['a', 'b', 'c'], ['d']]
  • compact 去除数组元素中的假值false, null,0, "", undefined, 和 NaN 都是被认为是“假值”
    • 参数1: array
_.compact([0, 1, false, 2, '', 3]);
// => [1, 2, 3]
  • concat : 创建一个新数组,将array与任何数组 或 值连接在一起
    • 参数1: array 被连接的数组
    • 参数n: [values]连接的值
var array = [1];
var other = _.concat(array, 2, [3], [[4]]);
 
console.log(other);
// => [1, 2, 3, [4]]
 
console.log(array);
// => [1]
  • memoize: 建一个会缓存 func 结果的函数。 如果提供了 resolver ,就用 resolver 的返回值作为 key 缓存函数的结果。 默认情况下用第一个参数作为缓存的 key。 func 在调用时 this 会绑定在缓存函数上。
    • 参数1:fucntion 需要缓存的函数
    • 参数2:key 这个函数的返回值作为缓存的 key。
const _ = require('lodash')

// 求圆的面积
function getArea (r) {
  console.log(r)
  return Math.PI * r * r
}

let getAreaWithMemory = _.memoize(getArea)

console.log(getAreaWithMemory(4))
console.log(getAreaWithMemory(4))
console.log(getAreaWithMemory(4))
console.log(getAreaWithMemory(4))
  • curry: 创建一个函数,该函数接收 func 的参数,要么调用func返回的结果,如果 func 所需参数已经提供,则直接返回 func 所执行的结果。或返回一个函数,接受余下的func 参数的函数,可以使用 func.length 强制需要累积的参数个数。
    • funciton 用来柯里化(curry)的函数
    • arg 需要提供给 func 的参数数量。
var abc = function(a, b, c) {
  return [a, b, c];
};
 
var curried = _.curry(abc);
 
curried(1)(2)(3);
// => [1, 2, 3]
 
curried(1, 2)(3);
// => [1, 2, 3]
 
curried(1, 2, 3);
// => [1, 2, 3]
 
// Curried with placeholders.
curried(1)(_, 3)(2);
// => [1, 2, 3]

纯函数的好处

  • 可缓存

    • 因为纯函数对相同的输入始终得到相同的输出,所以可以把纯函数的结果缓存起来
  • 可测试

    • 纯函数让测试跟方便
  • 并行处理

    • 在多线程环境下并行操作共享内存数据可能出现意外情况
    • 纯函数不需要访问共享内存数据,所以在并行环境下可以任意运行纯函数(web Worker )

副作用

副作用让一个函数变得不纯,纯函数根据相同的输入返回相同的输出,如果函数一类与外部的状态就无法保证输出相同,就会带来副作用

副作用来源

  • 配置文件
  • 数据库
  • 获取用户的输入

所有的外部交互都有可能带来副作用,副作用也使得方法通用性会下降不适合扩展和可重用性,同时副作用会给程序带来安全隐患和不确定性,但是副作用也不可能完全禁止,尽可能控制在可控范围内