编程范式是一个由思考问题以及实现问题愿景的工具组成的框架。不少现代语言都是聚范式(或者说多重范式): 他们支持不少不一样的编程范式,好比面向对象,元程序设计,泛函,面向过程,等等。javascript
函数式编程就像一辆氢燃料驱动的汽车——先进的将来派,可是尚未被普遍推广。与命令式编程相反,他由一系列语句组成,这些语句用于更新执行时的全局状态。函数式编程将计算转化做表达式求值。这些表达式全由纯数学函数组成,这些数学函数都是一流的(能够被当作通常值来运用和处理),而且没有反作用。html
函数式编程很重视如下值:java
咱们应该将函数与编程语言中的其余类对象一样对待。换句话说,您能够将函数存储在变量里,动态建立函数,以及将函数返回或者将函数传递给其余函数。下面咱们来看一个例子…程序员
一个字符串能够保存为一个变量,函数也能够,例如:算法
var sayHello = function() { return “Hello” };
一个字符串能够保存为对象字段,函数也能够,例如:编程
var person = {message: “Hello”, sayHello: function() { return “Hello” }};
一个字符串能够再用到时才建立,函数也能够,例如:数组
“Hello ” + (function() { return “World” })(); //=> Hello World
一个字符串能够做为输入参数传给函数,则函数也能够:安全
function hellloWorld(hello, world) { return hello + world() }
一个字符串能够做为函数返回值,函数也能够,例如:数据结构
return “Hello”; return function() { return “Hello”};
若是函数将其余函数函数做为输入参数或者做为返回值,则称之为高阶函数。刚才咱们已经看过了一个高阶函数的例子。下面,咱们来看一下更复杂的状况。多线程
例1:
[1, 2, 3].forEach(alert); // alert 弹窗显示“1" // alert 弹窗显示 "2" // alert 弹窗显示 "3”
例2:
function splat(fun) { return function(array) { return fun.apply(null, array); }; } var addArrayElements = splat(function(x, y) { return x + y }); addArrayElements([1, 2]); //=> 3
最爱纯函数
纯函数不会有其余的反作用,所谓的反作用指的是函数所产生的对函数外界状态的修改。好比:
最简单的例子就是数学函数。Math.sqrt(4) 函数老是返回2。他不会用到任何其余心寒信息,如状态或者设置参数。数学函数历来不会形成任何反作用。
函数式编程支持纯粹的函数,这样的函数不能改变数据,所以大多用于建立不可改变的的数据。这种方式,不用修改一个已存在的数据结构,并且能高效的新建一个.
你也许想知道,若是一个纯粹的函数经过改变一些本地数据而生产一个不可改变的返回值,是不是容许的?答案是能够。
在JavaScript中极少的数据类型是默认是不可改变的。String是一个不能被改变的数据类型的例子:
var s = "HelloWorld"; s.toUpperCase(); //=> "HELLOWORLD" s; //=> "HelloWorld"
• 避免混乱和增长程序的准确性:在复杂系统内,大多数难以理解的Bug是因为状态经过在程序中外部客户端代码修改而致使的。
• 确立“快速简洁”的多线程编程:若是多线程能够修改同一个共享值,你不得不一样步的获取值。这对专家来讲都是十分乏味而且易出错的编程挑战。
软件事务内存和Actor模型提供了直接在线程安全方式下处理修改。
递归是最有名的函数式编程技术。若是您还不知道它的话,那么能够理解为递归函数就是一个能够调用本身的函数。
替代反复循环的最经典方式就是使用递归,即每次完成函数体操做以后,再继续执行集合里的下一项,直到知足结束条件。递归还天生符合某些算法实现,好比遍历树形结构(每一个树枝都是一颗小树)。
在任何语言里,递归都是一项重要的函数式编程方式。不少函数语言甚至要求的更加严格:只支持递归遍历,而不支持显式的循环遍历。这须要语言必须保证消除了尾端调用,这是 JavasSrip 不支持的。
数学定义了不少无穷集合,好比天然数(全部的正整数)。他们都是符号表示。任意特定有限的子集都在须要时求值。咱们将其称之为惰性求值(也叫作非严格求值,或者按需调用,延迟执行)。及早求值会强迫咱们表示出全部无穷数据,而这显然是不可能的。
不少语言都默认是惰性的,有些也提供了惰性数据结构以表达无穷集合,并在须要时对本身进行精确计算。
很明显一行代码 result = compute() 所表达的是将 compute() 的返回结果赋值给 result。可是 result 的值到底是多少只有其被用到的时候才有意义。
可见策略的选择会在很大程度上提升性能,特别是当用在链式处理或者数组处理的时候。这些都是函数式程序员所喜好的编程技术。
这就开创可不少可能性,包括并发执行,并行技术以及合成。
可是,有一个问题,JavaScrip 并不对自身进行惰性求值。话虽如此,Javascript 里的函数库能够有效地模拟惰性求值。
全部的函数式语言都有闭包,然而这个语言特性常常被讨论得很神秘。闭包是一个函数,这个函数有着对内部引用的全部变量的隐式绑定。换句话说,该函数对它引用的变量封闭了一个上下文。JavaScript 中的闭包是可以访问父级做用域的函数,即便父级函数已经调用完毕。
function multiplier(factor) { return function(number) { return number * factor; }; } var twiceOf = multiplier(2); console.log(twiceOf(6)); //=> 12
函数式编程是声明式的,就像数学运算,属性和关系是定义好的。运行时知道怎么计算最终结果。阶乘函数的定义提供了一个例子:
factorial(n) = 1 if n = 1
n * factorial(n-1) if n > 1
该定义将 factorial(n) 的值关联到 factorial(n-1),是递归定义。特殊状况下的 factorial(1) 终止了递归。
var imperativeFactorial = function(n) { if(n == 1) { return 1 } else { product = 1; for(i = 1; i <= n; i++) { product *= i; } return product; } } var declarativeFactorial = function(n) { if(n == 1) { return 1 } else { return n * factorial(n - 1); } }
从它实现阶乘计算来看,声明式的阶乘可能看起来像“命令式”的,但它的结构更像声明式的。
命令式阶乘使用可变值、循环计数器和结果来累加计算后的结果。这个方法显式地实现了特定的算法。不像声明式版本,这种方法有许多可变步骤,致使它更难理解,也更难避免 bug 。
有不少函数式库:underscore.js, lodash,Fantasy Land, Functional.js, Bilby.js, fn.js, Wu.js, Lazy.js, Bacon.js, sloth.js, stream.js, Sugar, Folktale, RxJs 等等。
map(), filter(), 和 reduce()函数 构成了函数式程序员工具包的核心。 纯高阶函数成了函数式方法的主力。事实上,它们是纯函数和高阶函数应该仿效的典型。它们用一个函数做为输入,返回没有反作用的输出。