概述
今天介绍下函数式编程,在开始之前,先要了解一下什么是编程范式。编程范式,通俗解释就是编写代码的风格。常见的编程范式有命令式编程、声明式编程、以及函数式编程等。
命令式编程
编写代码,关注代码执行的步骤,即先做什么,再做什么。
声明式编程
通过代码编写,指定应该做什么,但不指定具体怎么去做。如Vue、React
函数式编程
函数式编程有两个特点:
- 函数是一等公民
- 函数是纯函数
在 JavaScript 中,函数是一等公民,函数跟其它数据类型一样使用,可以赋值给其它变量,也可以作为参数传入另一个函数,或者作为其它函数的返回值。纯函数是什么意思呢?
纯函数
定义
纯函数是函数式编程中非常重要的概念,纯函数有两个特点:
-
相同的输入总会得到相同的输出
-
执行过程中不会产生副作用
副作用是表示,表示在执行一个函数时,除了返回函数值之外,还对调用函数产生了附加的影响。比如修改全局变量、修改参数、或改变了外部的存储等。
// 输出确定、没有副作用,是纯函数
function sum(x, y) {
return x + y;
}
// 输出不确定,不是纯函数
function random(x) {
return Math.random() * x;
}
// 有副作用,不是纯函数
function setFontSize(el, color) {
el.style.color = color;
}
// 输出不确定、有副作用,不是纯函数
var count = 0;
function addCount(x) {
count += x;
return count;
}
纯函数的优点
- 因为你可以安心的编写和安心的使用
- 在写的时候保证了函数的纯度,只是单纯实现自己的业务逻辑即可,不需关心传入的内容是如何获取或者依赖其它的外部变量,是否已经发生了修改
- 用的时候,你确定输入的内容不会被任意篡改,并且自己确定的输入,一定会有确定的输出
柯里化
柯里化也是属于函数式编程里非常重要的概念。
定义
柯里化又称卡瑞化或加里化,是把接收多个参数的函数,变成接收一个单一参数(最初函数的第 1 个参数)的函数,并且返回接收余下参数,而且返回结果的新函数的技术。也属于函数式编程里非常重要的概念。
个人理解就是,只传递给函数一部分参数来调用它,让它返回一个函数去处理余下的参数,这个过程就是柯里化。
// 未柯里化的函数
function add1(x, y, z) {
return x + y + z;
}
var result1 = add1(10, 20, 30);
console.log(result1); // 60
// 柯里化处理的函数
function add2(x) {
return function (y) {
return function (z) {
return x + y + z;
};
};
}
var result2 = add2(10)(20)(30);
console.log(result2); // 60
// 柯里化处理的箭头函数
var add3 = (x) => (y) => (z) => x + y + z;
var result3 = add3(10)(20)(30);
console.log(result3); // 60
柯里化的优点
- 可以将一个函数处理的问题尽可能单一,而不是将一大堆的处理过程交给一个函数来处理
- 将每次传入的参数在单一的函数中进行处理,处理完成之后在下一个函数中再使用处理后的结果。
- 如下所示,对传入的参数分别进行处理
// 对传入的参数需要分别进行处理
function add(x) {
x = x + 2;
return function (y) {
y = y * 2;
return function (z) {
z = z ** 2;
return x + y + z;
};
};
}
var result = add(10)(20)(30);
console.log(result); // 952
- 可以复用业务逻辑
// makeAdder函数要求传一个num,在之后使用返回的函数时,就不需要再传入num了。
// 并且如果需要的话,可以在这里对num进行一些修改
function makeAdder(num) {
return function (count) {
return num + count;
};
}
var add5 = makeAdder(5);
var result1 = add5(10);
console.log(result1); // 15
var add10 = makeAdder(10);
var result2 = add10(10);
console.log(result2); // 20
柯里化的实现
如果每次都手动进行函数柯里化,书写会很麻烦,可以创建一个自动柯里化的函数,返回传入函数的柯里化结果
function myCurry(fn) {
function curried(...args) {
// 当已经传入的参数大于等于需要的参数时, 就执行函数
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
// 参数个数没有达到时, 需要返回一个新的函数, 继续来接收的参数
function curried2(...args2) {
// 接收到参数后, 需要递归调用curried
return curried.apply(this, [...args, ...args2]);
}
return curried2;
}
}
return curried;
}
function add(x, y, z) {
return x + y + z;
}
// 将add函数柯里化
curryAdd = myCurry(add);
console.log(curryAdd(10, 20, 30)); // 60
console.log(curryAdd(10)(20)(30)); // 60
console.log(curryAdd(10, 20)(30)); // 60
console.log(curryAdd(10)(20, 30)); // 60
组合函数
组合(compose)函数是在 JavaScript 开发过程中的一种函数的使用技巧、模式
- 比如现在需要对某一个数据进行函数调用,执行两个函数 fn1 和 fn2,这两个函数是依次调用的
- 如果每次我们都需要进行两个函数的调用,操作上会显得重复 是否可以将两个函数组合起来,自动依次调用呢?这个过程就是对函数的组合,称为组合函数(Compose Function)
function compose(fn1, fn2) {
return function (x) {
// 按传入顺序,从左往右,依次执行函数
return fn2(fn1(x));
};
}
function double(num) {
return num * 2;
}
function square(num) {
return num ** 2;
}
var calcFn = compose(double, square);
console.log(calcFn(10)); // 400
组合函数的实现
上述示例组合了两个函数的调用,为了更方便的创建组合函数,可以创建一个自动组合的函数,返回传入各函数组合后的函数
function myCompose(...fns) {
var length = fns.length;
for (var i = 0; i < length; i++) {
if (typeof fns[i] !== "function") {
throw new TypeError("Expected arguments are functions");
}
}
function compose(...args) {
// 按传入顺序,从左往右,依次执行函数
var index = 0;
var result = length ? fns[index].apply(this, args) : args;
while (++index < length) {
result = fns[index].call(this, result);
}
return result;
}
return compose;
}
function double(num) {
return num * 2;
}
function square(num) {
return num ** 2;
}
function add100(num) {
return num + 100;
}
var calcFn = myCompose(double, square, add100);
console.log(calcFn(10)); // 500 10 * 2 ** 2 + 10
下一篇,计划梳理 JavaScript 中的call、apply、bind
- 如果认为本篇知识点梳理的尚可,欢迎点赞
- 如果有需要补充、指正的地方,也欢迎评论留言哦~