函数式编程

778 阅读4分钟

这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战

函数式编程在如今前端领域越来越火,无论是React16、Vue3都在大量拥抱函数式编程,掌握函数式编程的理念和写法是前端必不可少的知识

优势

如今炙手可热的函数式编程具备哪些编程优势呢?

运行时间更快

相对于传统的面向过程、面向对象编程,运行速度更快

良好的测试性、减少的bug

相对比类而言,在编写单元测试时,函数式编程可以更好的支持测试用例,更好的提前发现问题和解决问题

调试方便

在debugger的,函数式编程可以更为直观的看到我们调试的过程,协助开发者快速定位问题和执行过程

更高的复用性

函数式编程可以将功能拆分粒度更小,每一个函数就是一个独立的功能模块,达到更好的复用性

更好的Tree-shaking支持

函数式编程可以大大降低副作用,利于Tree-shaking对我们的代码进行优化

对比

在我们写代码过程中,如果需要实现一个两数累加,不同的编程范式会有不同的写法

面向过程实现两数累加: 代码执行的顺序按照编写的顺序执行

const num1 = 1;
const num2 = 2;
const result = num1 + num2;

面向对象实现两数累加:封装Sum类并提供两数累加函数

const Sum = {
  add (num1, num2) {
    return num1 + num2;
  }
}

const result = Sum.add(1, 2);

函数式编程:提供一个两数累加的函数

函数是数学上的一个概念,本质是一种映射关系

const add = (num1, num2) => {
	return num1 + num2;
}

const result = add(1, 2);

基础

在理解函数式编程之前,需要了解三个概念协助我们更好的理解函数式编程

头等函数

当一门编程语言的函数可以被当作变量一样使用时,则称这门语言拥有头等函数

在JS中,函数可以被当作参数传递给其他函数,可以作为另外一个函数的返回值,可以被赋值给一个变量

函数可以被当作参数传递给其他函数

const fn = () => {
  // 逻辑代码
}

const composwe = (cb) => {
  // 逻辑处理
  return cb;
};

const res = composwe(fn);
res();

可以作为另外一个函数的返回值

const fn = () => {
  // 逻辑代码
}

const composwe = () => {
  // 逻辑处理
  return fn;
};

const res = composwe(fn);

可以被赋值给一个变量

function fn () {
  // 逻辑代码
}

const newFn = fn;

闭包

一个函数和对其周围状态(词法环境)的引用捆绑在一起(或者说函数倍引用包围),这样的组合就是闭包

function init() {
	const name = 'init name'; // name 是一个被 init创建的局部变量
  const fn = () => { // fn 是一个内部函数, 一个闭包
    console.log(name) // 使用父函数 init 中声明的变量 name
  }
  
  fn();
}

纯函数

纯函数在日常开发中非常常见,其具备两个特性

  1. 函数的返回结果只依赖它的参数,相同的输入始终得到相同的输出
  2. 函数执行过程中里面没有副作用 - 一个函数的执行过程中没有对外部产生可观察的变化,那么这个函数是没有副作用的

纯函数: 无论调用多少次,只要num1和num2不变,结果始终不变,且没有对外部产生影响

const add = (num1, num2) => num1 + num2;

非纯函数:不能保证结果始终相同,因为每次的输入是相同的

const getNum = () => Math.random() * 10;

非纯函数:产生了副作用

let bol = false;

const add = (num1, num2) => {
  const res = num1 + num2;
  bol = res > 100; // 产生了副作用
  
  return res;
};

纯函数的优点

可缓存

因为纯函数在相同的输入始终得到相同的输出,那么当我们将函数调用的参数进行缓存便可以直接将纯函数的调用结果进行缓存起来


const memoize = (fn, paramsHandler) => {
	  // 存在对象 - 存放参数和结果的对应关系
	  let cache = {};
  
		return (...args) => {
      const key = paramsHandler(...args);
      
      if(cache[key]) {
        console.log('已经缓存, 直接返回')
        return cache[key];
      }
      
      return (cache[key] = fn(...args));
    }
}

// 累加函数
const add = (num1, num2) => {
	return num1 + num2;
}

// 处理参数
const params = (...args) => JSON.stringify(args)

const newAdd = memoize(add, params);
const num1 = 1;
const num2 = 2;

const result1 = newAdd(num1, num2); // 3
const result2 = newAdd(num1, num2); // 3