JavaScript 函数式编程

133 阅读4分钟

编程范式

编程范式是开发者使用的高级语言所支持的一些变成个设计准则,这些设计准则约定了我们以什么样的方式将信息传达给机器环境。

命令式编程

命令式编程又称指令式编程、过程式编程。一般将命令式编程理解为顺序编程加一些控制流程的语句

面向对象

面向对象把计算机程序视为一组对象的集合,而每个对象都可以接受并处理其他对象发来的消息,换句话说,面向对象认为计算机程序的执行过程就是一系列消息在各个对象之间的传递。

元编程

在元编程模式下执行开发者编写的代码时,可以改变其他程序的行为,或者在运行代码时完成部分本应在代码编译时完成的工作

声明式编程

声明式编程在编码时关注的是要做成什么而不是怎么做;我们像管理者一样分配要做的事情,而不是亲力亲为地关注细节最有的 ROI 和效率

逻辑式编程

逻辑式编程是一种面向演绎推理的逻辑型程序设计语言,其中约束满足问题是变量集合和约束条件的集合,问题领域甚至可以一直扩展到人工智能级别

函数式编程

函数式编程将计算机运算视为数学上的函数计算,并且会避免使用程序状态以及易变对象

JavaScript 中的函数式

闭包

闭包指一个特殊的函数或方法,内里绑定了函数内部引用的所有变量

function fun() {
  let a = 'knight';
  return function fun2() {
    console.log(a);
  }
}

var b = fun()
b(); // knight

高阶函数

高阶函数函数的嵌套高阶调用,即实参或返回值为函数

function fun(fun2) {
  fun2();
}

fun(function () {
  console.log('knight');
}) // knight

Lambada 表达式

Lambad 表达式在前端以箭头函数的表现形式出现。

var fun = () => {
  console.log('knight');
}

fun(); // knight

Array 数组集合和函数组合子

JavaScript 通过函数组合子使关注结果的方法取代命令式里常见的详细描述操作过程的保存游标、循环调用等方法

var a = [1, 2, 3, 4].map(value => value * 3).filter(value => value % 2 === 1).join(' ');
console.log(a); // 3 9

编码优化

惰性求值

惰性求值支持在程序运行优化时,消解掉一部分不必要执行的代码

JavaScript 中我们可以使用 thunk 函数实现惰性求值,也就是把要运行的代码放在一个未执行的函数中,手动控制函数的调用事件

const x = 1, y = 2, z = 5;
const plusThunk = function () {
  return x + z;
}
const multiplyThunk = function () {
  return y * z;
}

const squares = x => x * x;
function sumOfSquares(tempFuncA, tempFuncB) {
  return squares(tempFuncA()) + squares(tempFuncB());
}
console.log(sumOfSquares(plusThunk, multiplyThunk)); // 136

柯里化和偏函数

柯里化时将多个参数直接打散,每次只传一个实参,对参数赋值后,都可以产生新的部分施用函数

偏函数与柯里化类似,只不过每次传一部分实参

函数式的抽象单元

副作用

副作用指在程序运行中,不止对传入值操作产生传出值,还对这两个值以外的部分产生了影响

引用透明和纯函数

当函数没有明显的副作用,且没有隐性的依赖时,函数相同的入参能够输出相同的结果,这样的函数我们称为纯函数

如果在环境 b 中调用 funcA() ,可以在运行环境中替换称结果 r ,而不改变 b 的含义,我们就可以说 funcA() 对环境时引用透明的

不可变数据结构

不可变数据结构包含两个特性:不可变性和持久性

不可变性意味着数据结构一旦创建就不能被修改了;持久性是指当尝试该百年不可变数据结构时,总会返回一个建立在旧数据基础上的新数据结构

在 JavaScript 中 String 数据结构本身是不可变的

尾调用优化

如果每次递归尾部返回的内容都是一个带计算的表达式,那么运行时的内存栈中会一直压入等待计算的变量和环境,这样可能会触发堆栈调用超限的提醒

而我们将程序写法变为返回新的递归方法,函数的调用就可以等价替换为返回的结果

function factorial(n) {
  'use strict'
  if (n <= 1) {
    return 1
  } else {
    let a = n - 1; 
    return factorial(a);
  }
}
factorial(10000);

但是尾调用优化浏览器支持并不好,且需要使用严格模式