一 历史和现状
函数式编程的起源,是一门叫做范畴论(Category Theory)的数学分支。理解函数式编程的关键,就是理解范畴论。它是一门很复杂的数学,认为世界上所有的概念体系,都可以抽象成一个个的”范畴”(category)。什么是范畴呢?也就是说,彼此之间存在某种关系的概念、事物、对象等等,都构成”范畴”。随便什么东西,只要能找出它们之间的关系,就能定义一个”范畴”。本质上,函数式编程只是范畴论的运算方法,跟数理逻辑、微积分、行列式是同一类东西,都是数学方法,只是碰巧它能用来写程序。所以,为什么函数式编程要求函数必须是纯的,不能有副作用?因为它是一种数学运算,原始目的就是求值,不做其他事情,否则就无法满足函数运算法则了。(来源于阮一峰大佬的网络博客,地址如下:https://ruanyifeng.com/blog/2017/02/fp-tutorial.html)
随着react越来越流行,关注度变高;vue3开始拥抱函数式编程;函数式编程可以抛弃this;打包过程中可以更好的tree shaking;方便测试,方便并行处理;有些库:lodash、underscore、ramda
二 定义
FP(Functional Programming),编程范式之一,其他范式如面向对象、面向过程;
面向对象: 现实的事物抽象成类和对象、通过封装、继承、多态来演示事物的复杂性;
函数式编程:对运算过程抽象,抽象事物和事物之间的联系;
程序的本质: 根据输入运算后得到输出,重点在于x->y的过程
注意: 函数在里面是数学中的函数映射关系,不是程序中的函数(方法);
相同的输入要得到相同的输出;(纯函数)
函数式编程用来描述数据(函数)之间的联系;
示例:
// 非函数式编程
let a = 1;
let b = 2;
let sum = a + b;
// 函数式编程
function add(a, b) {
return a + b;
}
let sum = add(1, 2);
三 First-class Funciton 一等公民
js中函数是个对象所以,函数可以存储在变量中;函数可以作为参数;函数作为返回值;也可以在运行中通过new function(){}来构造一个新的函数;
函数存储变量示例
let fn = function() {
console.log('fn')
}
// 函数参数和返回值相同时,视为同一个函数
// 示例
// const handler = {
// index (a) {
// return View.index(a);
// },
// show (b) {
// return View.show(b);
// }
// }
// 优化
const handler = {
index: View.index,
show: View.show
}
函数作为参数或者返回值(高阶函数)示例
// 函数作为参数
function forEach(arr, fn) {
for(let i=0;i++;i<arr.length) {
fn(arr[i], i);
}
}
function filter (arr, fn) {
let result = [];
for(let i=0;i<arr.length;i++){
if(fn(arr[i])) {
result.push(arr[i]);
}
}
return result;
}
// 函数作为返回值
function makeFn () {
let msg = 'hello';
return function () {
console.log(msg);
}
}
function once (fn) {
let flag = true;
return function() {
if(flag == true) {
fn.apply(this, arguments);
flag = false;
}
}
}
// 复习下apply
// let a = {
// x: 1,
// y: function() {
// console.log(this.x);
// }
// }
// let b = {
// x: 2
// }
// a.y.apply(b);
// 2
使用高阶函数的意义
抽象通用的问题,屏蔽细节,关注目标
常用的高阶函数 forEach、filter、some、every、map、find、findIndx等;模拟示例如下
// some
function some (arr, fn) {
let flag = false;
for(let i=0;i<arr.length;i++) {
if(fn(arr[i])){
flag = true;
break;
}
}
return flag;
}
// every
function every (arr, fn) {
let flag = true;
for(let i=0;i<arr.length;i++) {
if(fn(arr[i])) {
flag = false;
break;
}
}
return flag;
}
// map
function map (arr, fn) {
let result = [];
for(let i=0;i<arr.length;i++) {
result.push(fn(arr[i]))
}
return result;
}
// find
function find (arr, fn) {
let item = false;
for(let i=0;i<arr.length;i++) {
if(fn(arr[i])) {
item = arr[i];
}
break;
}
return item;
}
// findindx
function findIndex (arr, fn) {
let index;
for(let i=0;i<arr.length;i++) {
if(fn(arr[i])) {
index = i;
break;
}
}
return index;
}
四 闭包
函数和其周围的状态(词法环境)的引用捆绑在一起形成闭包;可以在另一个作用域中调用一个函数的内部函数并且可以访问到该函数的作用域的成员;
闭包的本质:函数在执行的时候会放到一个执行栈上,当函数执行完毕后会从栈上移除;但是堆上的成员因为外部引用不能被释放,因此内部函数依然可以访问外部函数的成员;延长了外部函数内部的作用范围;
闭包案例
let power = function(pow) {
return function(num){
return Math.pow(num, pow)
}
}
let power2 = power(2);
let power3 = power(3);