带你入门函数式编程

带你入门函数式编程

1、什么是函数式编程

1.概念

首先,函数式编程是一种编程范型。其余的编程范型有面向过程编程(关注计算机的执行步骤,指定先作这个再作那个),还有面向对象(建立对象而后对象又有方法而后又能够改变它们)等等。函数式编程也是编程范型,函数为王。**javascript

这里须要介绍下编程范型的概念,编程范型/编程范式(范即模范、典范之意,范型即模式、方法)是一类典型的编程风格,是指从事软件工程的一类典型的风格。如函数式编程、过程式编程、面向对象编程、指令式编程等等为不一样的编程范型。
编程范型提供了(同时决定了)程序员对程序执行的见解。例如,在面向对象编程中,程序员认为程序是一系列相互做用的对象,而在函数式编程中一个程序会被看做是一个无状态的函数计算的序列。html

函数式编程也是一种编程风格,完成项目时怎样组织编写代码。
函数式编程更是一种观念,能够以一种思考问题的方式来完成任务,也是一种趋势前端

2.优势

  • 更安全,更容易调试,构建项目时更好去维护:函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果一定相同。所以,每个函数均可以被看作独立单元,颇有利于进行单元测试和除错,以及模块化组合。
  • 有庞大的使用函数式编程开发JS的开发者社区
  • 代码简洁,抽象程度高,容易复用,开发快速:函数式编程大量使用函数,减小了代码的重复,所以程序比较短,开发速度较快,抽象程度高,也更容易复用。
  • 接近天然语言,易于理解:函数式编程的自由度很高,能够写出很接近天然语言的代码。如如下代码:(merge([1,2],[3,4]).sort().search("2"),应该一眼就能明白它的意思。
  • 易于"并发编程":函数式编程不须要考虑"死锁",由于它不修改变量,因此根本不存在"锁"线程的问题。没必要担忧一个线程的数据,被另外一个线程修改,因此能够很放心地把工做分摊到多个线程,部署"并发编程"。

3.使用指南

(1)在函数式编程中,一切都要函数化

要用函数实现程序,给定输入,会有一个输出,咱们更多的要考虑数据的输入输出流,须要想怎样用函数表达一切。java

  • 非函数化:(命令式风格)
const name = "ming";
const greeting = "hello,";
console.log(greeting + name); // hello,ming
  • 函数化:
function greet(name) {
	return `hello,${name}`;
}
greet("ming"); // hello,ming

(2)使用纯函数,避免反作用

反作用:反作用是指在计算过程当中,系统状态的一种变化,或者是与外部进行的可观察的交互。
反作用可能包含,但不限于:git

  • 更改文件系统
  • 往数据库插入记录
  • 发送一个 http 请求
  • 可变数据
  • 打印/log
  • 获取用户输入
  • DOM 查询
  • 访问系统状态

函数式编程的哲学就是假定反作用是形成不正当行为的主要缘由。
固然这并非说,要禁止使用一切反作用,而是说,要让它们在可控的范围内发生。程序员

纯函数:针对相同的一组输入,会永远获得相同的输出,并且没有任何可观察的反作用,这样的函数叫纯函数。
用一个例子来讲明下。数组里的slice和splice方法,这两个函数的做用是同样的,可是须要注意的是,splice会改变原数组,而slice不会改变原数组。因此slice每次用相同的输入去执行都会获得相同的输出,符合纯函数的定义。而splice改变了原数组,每次用相同的输入去执行都会获得不一样的输出,产生了反作用。github

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;
};

在不纯的版本中,函数外部的变量会影响到函数的返回结果,或者说它引入了外部的环境。
而在纯的版本中,函数就能够本身保持“独立”。编程

(3)使用高阶函数:函数能够做为输入/输出

function makeAdjectifier(adjective) {
    return function (string) {
        adjective + " " + string;
    }; 
}
var coolifier = makeAdjectifier("cool");
coolifer("conference");  // => "cool conference"

不要使用for/while等迭代,使用map,reduce,filter这样的高阶函数,能够做为一个函数去应用。数组

(4)避免可变性:使用不可变数据

很差的作法:

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']

(5)持久的数据结构,实现高效的不变性

当咱们将事物视为不变时,咱们最终所作的事情就是为全部事物制做新的副本。
若是有一个数组[1,2,3],咱们要改变3为4,那么在可变的环境中直接把3换为4便可,可是以前说过要避免这种操做,就要取而代之,复制一个新数组,复制1,2而后输入4,就须要花更多时间存储两个数组,这是很是可怕的。咱们能够把这个数组当作一棵树,树上的叶子结点就是要存储东西的地方,若是想改变某个数据,只须要制造一个新节点存4,能够新建一个有1,2,4的树,能够服用以前已经存在的数据。这个叫作数据共享。这样咱们能够不用浪费不少时间和存储空间去更新数据。

2、柯里化

概念:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。

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。

3、组合

组合其实就是你能够选择两个函数,让它们结合,产生一个新的函数。这样写可使咱们的代码更加优雅。

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的分享,不止于前端,按期输出一些技术知识、生活感想、理财知识等。

相关文章
相关标签/搜索