首先,函数式编程是一种编程范型。其他的编程范型有面向过程编程(关注计算机的执行步骤,指定先做这个再做那个),还有面向对象(创建对象然后对象又有方法然后又可以改变它们)等等。函数式编程也是编程范型,函数为王。**
这里需要介绍下编程范型的概念,编程范型/编程范式(范即模范、典范之意,范型即模式、方法)是一类典型的编程风格,是指从事软件工程的一类典型的风格。如函数式编程、过程式编程、面向对象编程、指令式编程等等为不同的编程范型。
编程范型提供了(同时决定了)程序员对程序执行的看法。例如,在面向对象编程中,程序员认为程序是一系列相互作用的对象,而在函数式编程中一个程序会被看作是一个无状态的函数计算的序列。
函数式编程也是一种编程风格,完成项目时怎样组织编写代码。
函数式编程更是一种观念,可以以一种思考问题的方式来完成任务,也是一种趋势。
要用函数实现程序,给定输入,会有一个输出,我们更多的要考虑数据的输入输出流,需要想怎样用函数表达一切。
const name = "ming";
const greeting = "hello,";
console.log(greeting + name); // hello,ming
function greet(name) {
return `hello,${name}`;
}
greet("ming"); // hello,ming
副作用:副作用是指在计算过程中,系统状态的一种变化,或者是与外部进行的可观察的交互。
副作用可能包含,但不限于:
函数式编程的哲学就是假定副作用是造成不正当行为的主要原因。
当然这并不是说,要禁止使用一切副作用,而是说,要让它们在可控的范围内发生。
纯函数:针对相同的一组输入,会永远得到相同的输出,而且没有任何可观察的副作用,这样的函数叫纯函数。
用一个例子来说明下。数组里的slice和splice方法,这两个函数的作用是一样的,但是需要注意的是,splice会改变原数组,而slice不会改变原数组。所以slice每次用相同的输入去执行都会得到相同的输出,符合纯函数的定义。而splice改变了原数组,每次用相同的输入去执行都会得到不同的输出,产生了副作用。
let arr = [1,2,3,4,5];
// 纯的
arr.slice(0,3); // [1,2,3]
arr.slice(0,3); // [1,2,3]
arr.slice(0,3); // [1,2,3]
// 不纯的
arr.splice(0,3); // [1,2,3]
arr.splice(0,3); // [4,5]
arr.splice(0,3); // []
再看另外一个例子:
// 不纯的
var b = 21;
var test = function(a) {
return a >= b;
};
// 纯的
var test = function(a) {
var b = 21;
return a >= b;
};
在不纯的版本中,函数外部的变量会影响到函数的返回结果,或者说它引入了外部的环境。
而在纯的版本中,函数就可以自己保持“独立”。
function makeAdjectifier(adjective) {
return function (string) {
adjective + " " + string;
};
}
var coolifier = makeAdjectifier("cool");
coolifer("conference"); // => "cool conference"
不要使用for/while等迭代,使用map,reduce,filter这样的高阶函数,可以作为一个函数去应用。
不好的做法:
let arr = [‘1‘,‘2‘,‘3‘];
arr[2] = ‘4‘;
console.log(arr); // [‘1,‘2‘,‘4‘]
好的做法:
const arr = [‘1‘,‘2‘,‘3‘];
const newArr = arr.map(item => {
if(item === ‘3‘) {
return ‘4‘;
}
return item;
});
console.log(arr); // [‘1,‘2‘,‘3‘]
console.log(newArr); // [‘1,‘2‘,‘4‘]
当我们将事物视为不变时,我们最终所做的事情就是为所有事物制作新的副本。
如果有一个数组[1,2,3],我们要改变3为4,那么在可变的环境中直接把3换为4即可,但是之前说过要避免这种操作,就要取而代之,复制一个新数组,复制1,2然后输入4,就需要花更多时间存储两个数组,这是非常可怕的。我们可以把这个数组当做一棵树,树上的叶子结点就是要存储东西的地方,如果想改变某个数据,只需要制造一个新节点存4,可以新建一个有1,2,4的树,可以服用之前已经存在的数据。这个叫做数据共享。这样我们可以不用浪费很多时间和存储空间去更新数据。
概念:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
var add = function(x) {
return function(y) {
return x + y;
};
};
var increment = add(1);
var addTen = add(10);
increment(2);
// 3
addTen(2);
// 12
上面我们定义了一个add函数,它接收一个参数,并返回一个新的函数。调用add后,返回的函数就以闭包的方式记住了它的第一个参数x。
组合其实就是你可以选择两个函数,让它们结合,产生一个新的函数。这样写可以使我们的代码更加优雅。
var compose = function(f,g) {
return function(x) {
return f(g(x));
};
};
f 和 g 都是函数,x 是在它们之间通过“管道”传输的值。
var toUpperCase = function(x) { return x.toUpperCase(); };
var exclaim = function(x) { return x + ‘!‘; };
var shout = compose(exclaim, toUpperCase);
shout("send in the clowns");
//=> "SEND IN THE CLOWNS!"
参考:
Learning Functional Programming with JavaScript, by Anjana Vakil — JSUnconf 2016
函数式编程初探
编程范型wiki
函数式编程指北中文版
更多文章以及分享请关注微信公众号 前端er的分享,不止于前端,定期输出一些技术知识、生活感想、理财知识等。
原文:https://www.cnblogs.com/ym7583306/p/14002838.html