纯函数概念

相同的输入永远都是相同的输出,并且没有任何可观察的副作用(什么是副作用后面会讲到)

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

lodash就是一个纯函数的功能库,提供了对数组,数字,字符串,对象,函数等一连串的操作方法

举个例子:splice和slice

  • slice返回数组中的指定部分,不改变原来的数组,是纯函数
  • splice对数组进行操作并且返回此数组,会改变原来的数组,是不纯的函数
// 纯函数slice,不纯的函数splice
let array = [1, 2, 3, 4, 5];

// 三次执行都是一样的返回值 [1,2,3]
console.log(array.slice(0, 3));
console.log(array.slice(0, 3));
console.log(array.slice(0, 3));

let spliceArr = [1, 2, 3, 4, 5, 6, 7, 8];

// 三次执行之后的每次结果都不一样
// [1,2] [3,4] [5,6]
console.log(spliceArr.splice(0, 2));
console.log(spliceArr.splice(0, 2));
console.log(spliceArr.splice(0, 2));
  • 函数式编程不会保留计算中间的结果,所以变量是不可变的(没有状态的)
  • 我们可以把一个函数的执行结果交给另一个函数处理

Lodash:纯函数代表

lodash
这里先简单的提一下这个工具,后面讲解柯里化的时候会再次提及

纯函数的好处

1.可缓存
因为纯函数的特性:相同的输入始终有相同的输出,所以可以把纯函数的结果缓存起来

const _ = require("lodash");

const getCircleArea = (r) => {
  console.log(r);
  return Math.PI * r * r;
};

let getAreaWithMemory = _.memoize(getCircleArea);
console.log(getAreaWithMemory(4));
console.log(getAreaWithMemory(4));
console.log(getAreaWithMemory(4));

这段代码中,在getCircleArea中打印了一个r,之后的返回结果其实显而易见的是:

为什么会这样?因为第一次执行之后数据已经丢入了缓存中,再次执行相同的输入,是直接从缓存中拿取结果。这个演示代码太过于简单,所以直观上来说用这种办法演示是最有效的

模拟memoize:

function memoize(fn) {
  // 存储执行结果
  let cache = {};
  return function () {
    let key = JSON.stringify(arguments);
    cache[key] = cache[key] || fn.apply(fn, arguments);
    return cache[key];
  };
}

const getCircleArea = (r) => {
  console.log(r);
  return Math.PI * r * r;
};

let getAreaWithMemory = memoize(getCircleArea);
console.log(getAreaWithMemory(4));
console.log(getAreaWithMemory(4));
console.log(getAreaWithMemory(4));

上述代码,工作原理如下:

  1. 创建一个变量 cache 用于存储函数执行结果
  2. 返回一个新的函数,该函数会先将传入的参数使用 JSON.stringify 方法转为字符串类型,并作为 key 存储在 cache 对象中
  3. 如果 cache 中已经存在相同的 key,则直接返回缓存的结果;否则调用原始函数 fn 执行并将结果存储到 cache 中再返回

这样,在多次调用函数时,如果传入相同的参数,则从缓存中获取结果返回,避免了重复计算,提高了函数的执行效率。

2.可测试
纯函数让测试更加的方便
3.方便并行处理

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

副作用

回顾一下纯函数的定义:对于相同的输入永远都有着相同的输出,并且不会有任何可观察的副作用

// 不纯的
let miniAge = 18;
function checkAge (age) {
    return age >= miniAge
}

// 纯函数(有硬编码,柯里化可以解决)
function checkAgeClean (age) {
    let miniAgeClean = 18;
    return age >= miniAgeClean
}

Tips:什么叫硬编码?就是有具体的值,这在编程里是极其糟糕的。

副作用让一个函数变的不纯(如上例),纯函数的根据相同的输入返回相同的输出,如果函数依赖于外部的状态就无法保证输出相同,就会带来副作用。

副作用的来源:

  • 获取用户输入
  • 数据库
  • 配置文件啊
  • eg....

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

柯里化(Haskell Brooks Curry)

上面的代码里提到了如何解决硬编码的方法,就是柯里化

// 柯里化解决硬编码
function checkAge(age) {
  let miniAge = 18;
  return age >= miniAge;
}

// 普通的纯函数
function cleanCheckAge(miniAge, age) {
  return age >= miniAge;
}

// 柯里化
function checkAgeCurry(min) {
  return function (age) {
    return age >= min;
  };
}

let checkAge18 = checkAgeCurry(18);

console.log(checkAge18(20));

// ES6写法
const checkAgeCurryES6 = (min) => (age) => age >= min;

柯里化:

  • 当一个函数有多个参数的时候先传递一部分参数调用它(这部分的参数以后永远不变)
  • 然后返回一个新的函数接收剩下的参数,返回结果
最后修改:2024 年 03 月 21 日
收款不要了,给孩子补充点点赞数吧