探究JavaScript函数式编程的乐趣


Posted in Javascript onDecember 14, 2015

编程范式

编程范式是一个由思考问题以及实现问题愿景的工具组成的框架。很多现代语言都是聚范式(或者说多重范式): 他们支持很多不同的编程范式,比如面向对象,元程序设计,泛函,面向过程,等等。

探究JavaScript函数式编程的乐趣

函数式编程范式

函数式编程就像一辆氢燃料驱动的汽车——先进的未来派,但是还没有被广泛推广。与命令式编程相反,他由一系列语句组成,这些语句用于更新执行时的全局状态。函数式编程将计算转化作表达式求值。这些表达式全由纯数学函数组成,这些数学函数都是一流的(可以被当做一般值来运用和处理),并且没有副作用。

探究JavaScript函数式编程的乐趣

函数式编程很重视以下值:
函数是一等要务

我们应该将函数与编程语言中的其他类对象同样对待。换句话说,您可以将函数存储在变量里,动态创建函数,以及将函数返回或者将函数传递给其他函数。下面我们来看一个例子...

 探究JavaScript函数式编程的乐趣

一个字符串可以保存为一个变量,函数也可以,例如:

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

高阶案例

探究JavaScript函数式编程的乐趣

如果函数将其他函数函数作为输入参数或者作为返回值,则称之为高阶函数。刚才我们已经看过了一个高阶函数的例子。下面,我们来看一下更复杂的情况。
例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

最爱纯函数

探究JavaScript函数式编程的乐趣

纯函数不会有其他的副作用,所谓的副作用指的是函数所产生的对函数外界状态的修改。比如:

  • 修改某个变量
  • 修改数据结构
  • 对外界某个变量设置字段
  • 抛出例外或者弹出错误信息

最简单的例子就是数学函数。Math.sqrt(4) 函数总是返回2。他不会用到任何其他心寒信息,如状态或者设置参数。数学函数从来不会造成任何副作用。

避免修改状态


探究JavaScript函数式编程的乐趣

函数式编程支持纯粹的函数,这样的函数不能改变数据,因此大多用于创建不可改变的的数据。这种方式,不用修改一个已存在的数据结构,而且能高效的新建一个.
你也许想知道,如果一个纯粹的函数通过改变一些本地数据而生产一个不可改变的返回值,是否是允许的?答案是可以。
在JavaScript中极少的数据类型是默认是不可改变的。String是一个不能被改变的数据类型的例子:

var s = "HelloWorld";
  s.toUpperCase();
  //=> "HELLOWORLD"
  s;
  //=> "HelloWorld"

不可改变状态的好处

    •    避免混乱和增加程序的准确性:在复杂系统内,大多数难以理解的Bug是由于状态通过在程序中外部客户端代码修改而导致的。

    •    确立“快速简洁”的多线程编程:如果多线程可以修改同一个共享值,你不得不同步的获取值。这对专家来说都是十分乏味并且易出错的编程挑战。

软件事务内存和Actor模型提供了直接在线程安全方式下处理修改。

使用递归而非循环调用

探究JavaScript函数式编程的乐趣

递归是最有名的函数式编程技术。如果您还不知道它的话,那么可以理解为递归函数就是一个可以调用自己的函数。
替代反复循环的最经典方式就是使用递归,即每次完成函数体操作之后,再继续执行集合里的下一项,直到满足结束条件。递归还天生符合某些算法实现,比如遍历树形结构(每个树枝都是一颗小树)。
在任何语言里,递归都是一项重要的函数式编程方式。很多函数语言甚至要求的更加严格:只支持递归遍历,而不支持显式的循环遍历。这需要语言必须保证消除了尾端调用,这是 JavasSrip 不支持的。

惰性求值优于激进计算

探究JavaScript函数式编程的乐趣

数学定义了很多无穷集合,比如自然数(所有的正整数)。他们都是符号表示。任意特定有限的子集都在需要时求值。我们将其称之为惰性求值(也叫做非严格求值,或者按需调用,延迟执行)。及早求值会强迫我们表示出所有无穷数据,而这显然是不可能的。
很多语言都默认是惰性的,有些也提供了惰性数据结构以表达无穷集合,并在需要时对自己进行精确计算。
很明显一行代码 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 。

探究JavaScript函数式编程的乐趣

函数式JavaScript库

有很多函数式库: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()函数 构成了函数式程序员工具包的核心。 纯高阶函数成了函数式方法的主力。事实上,它们是纯函数和高阶函数应该仿效的典型。它们用一个函数作为输入,返回没有副作用的输出。
这些 JavaScript 函数对每一个函数式程序来说都是至关重要的。他们可以去除循环和语句,使得代码更加整洁。这些都是实现 ECMAScript5.1 的浏览器的标准,他们只处理数组。每次调用都会创建创建并返回一个新的数组。已存在的数组不会被修改。但是稍等,事情很不止于此。。。他们还将函数作为输入参数,通常是作为回调的匿名函数。他们会遍历将整个数组并且将该回调函数应用与每一项!
myArray = [1,2,3,4];
newArray = myArray.map(function(x) {return x*2});
console.log(myArray); // Output: [1,2,3,4]
console.log(newArray); // Output: [2,4,6,8]
除了这三个函数,还有很多函数可以扎入到几乎每一个函数式应用里:
forEach(),concat(), reverse(), sort(), every() 以及some().
JavaScript的聚范式

JavaScript当然不是严格意义上的函数式编程语言,这也促使了对其他范式的使用:
命令式编程:基于详细操作描述式的编程
基于原型的面向对象式编程:基于原型对象及其实例的编程
元程序编程:操纵JavsScript执行模型的编程方式。对元程序编程的一个很好的定义描述为“编程发生在您书写代码做某事的时候,而元程序编程则发生在您书写代码导致某事的解释方式发生变化的时候。

以上就是JavaScript函数式编程的使用方法,希望对大家的学习有所帮助。

Javascript 相关文章推荐
jQuery DOM操作小结与实例
Jan 07 Javascript
jquery监听div内容的变化具体实现思路
Nov 04 Javascript
JavaScript中判断对象类型的几种方法总结
Nov 11 Javascript
jquery 插件实现多行文本框[textarea]自动高度
Mar 04 Javascript
JavaScript限定图片显示大小的方法
Mar 11 Javascript
javascript实现给定半径求出圆的面积
Jun 26 Javascript
EditPlus中的正则表达式 实战(4)
Dec 15 Javascript
详解Vue学习笔记进阶篇之列表过渡及其他
Jul 17 Javascript
Layui 导航默认展开和菜单栏选中高亮设置的方法
Sep 04 Javascript
微信小程序以ssm做后台开发的实现示例
Apr 08 Javascript
微信小程序学习总结(一)项目创建与目录结构分析
Jun 04 Javascript
JS前端可视化canvas动画原理及其推导实现
Aug 05 Javascript
jQuery+json实现的简易Ajax调用实例
Dec 14 #Javascript
以JavaScript来实现WordPress中的二级导航菜单的方法
Dec 14 #Javascript
JS实现DIV容器赋值的方法
Dec 14 #Javascript
JavaScript中eval()函数用法详解
Dec 14 #Javascript
JS实现单击输入框弹出选择框效果完整实例
Dec 14 #Javascript
JavaScript实现点击按钮就复制当前网址
Dec 14 #Javascript
JavaScript 实现的 zip 压缩和解压缩工具包Zip.js使用详解
Dec 14 #Javascript
You might like
destoon切换城市后实现logo旁边显示地区名称的方法
2014/08/21 PHP
PHP连接和操作MySQL数据库基础教程
2014/09/29 PHP
PHP中使用socket方式GET、POST数据实例
2015/04/02 PHP
使用PHP下载CSS文件中的所有图片【几行代码即可实现】
2016/12/14 PHP
ThinkPHP框架表单验证操作方法
2017/07/19 PHP
详解将数据从Laravel传送到vue的四种方式
2019/10/16 PHP
js获取当前select 元素值的代码
2010/04/19 Javascript
理解Javascript_14_函数形式参数与arguments
2010/10/20 Javascript
JQuery中$.each 和$(selector).each()的区别详解
2015/03/13 Javascript
jQuery拖拽插件gridster使用指南
2015/04/21 Javascript
基于AngularJS实现页面滚动到底自动加载数据的功能
2015/10/16 Javascript
详解JavaScript树结构
2017/01/09 Javascript
原生js jquery ajax请求以及jsonp的调用方法
2017/08/04 jQuery
Angular实现点击按钮后在上方显示输入内容的方法
2017/12/27 Javascript
JS非行间样式获取函数的实例代码
2018/06/05 Javascript
vue组件横向树实现代码
2018/08/02 Javascript
浅谈关于JS下大批量异步任务按顺序执行解决方案一点思考
2019/01/08 Javascript
echarts统计x轴区间的数值实例代码详解
2019/07/07 Javascript
基于Vue中使用节流Lodash throttle详解
2019/10/30 Javascript
JS实现百度搜索框关键字推荐
2020/02/17 Javascript
javascript canvas检测小球碰撞
2020/04/17 Javascript
[02:12]DOTA2英雄基础教程 变体精灵
2013/12/16 DOTA
在Python中使用M2Crypto模块实现AES加密的教程
2015/04/08 Python
使用Python生成随机密码的示例分享
2016/02/18 Python
python验证码识别教程之利用滴水算法分割图片
2018/06/05 Python
Pytorch的mean和std调查实例
2020/01/02 Python
python调用HEG工具批量处理MODIS数据的方法及注意事项
2020/02/18 Python
Pandas之read_csv()读取文件跳过报错行的解决
2020/04/21 Python
小白教你PyCharm从下载到安装再到科学使用PyCharm2020最新激活码
2020/09/25 Python
Expedia印度尼西亚站:预订酒店、廉价航班和度假套餐
2018/01/31 全球购物
物流专业大学应届生求职信
2013/11/03 职场文书
正规的求职信范文分享
2013/12/11 职场文书
给导游的表扬信
2014/01/10 职场文书
巡警年度自我鉴定
2014/02/21 职场文书
有限公司股东合作协议书
2014/10/29 职场文书
小程序wx.getUserProfile接口的具体使用
2021/06/02 Javascript