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

高阶案例

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

例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 。

函数式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()函数 构成了函数式程序员工具包的核心。 纯高阶函数成了函数式方法的主力。事实上,它们是纯函数和高阶函数应该仿效的典型。它们用一个函数作为输入,返回没有副作用的输出。

时间: 2024-08-25 21:44:37

javascript 函数式编程的相关文章

javaScript函数式编程

第1章 JavaScript函数式编程简介 11.1 JavaScript案例 11.2 开始函数式编程 41.2.1 为什么函数式编程很重要 41.2.2 以函数为抽象单元 71.2.3 封装和隐藏 91.2.4 以函数为行为单位 101.2.5 数据抽象 141.2.6 函数式JavaScript初试 171.2.7 加速 191.3 Underscore示例 221.4 总结 23第2章 一等函数与Applicative编程 242.1 函数是一等公民 242.2 Applicative编

JavaScript函数式编程(三)

JavaScript函数式编程(一) JavaScript函数式编程(二) 在第二篇文章里,我们介绍了 Maybe.Either.IO 等几种常见的 Functor,或许很多看完第二篇文章的人都会有疑惑: 『这些东西有什么卵用?』 事实上,如果只是为了学习编写函数式.副作用小的代码的话,看完第一篇文章就足够了.第二篇文章和这里的第三篇着重于的是一些函数式理论的实践,是的,这些很难(但并非不可能)应用到实际的生产中,因为很多轮子都已经造好了并且很好用了.比如现在在前端大规模使用的 Promise

20170917 前端开发周报:JavaScript函数式编程、作用域和闭包

1.用函数式编程对JavaScript进行断舍离 当从业20的JavaScript老司机学会函数式编程时,他扔掉了90%的特性,也不用面向对象了,最后发现了真爱啊!!! https://juejin.im/entry/59b86... 2.JavaScript作用域和闭包 作用域和闭包在JavaScript里非常重要.但是在我最初学习JavaScript的时候,却很难理解.这篇文章会用一些例子帮你理解它们.我们先从作用域开始.作用域 JavaScript的作用域限定了你可以访问哪些变量.有两种作

JavaScript函数式编程(1):基本思想

1 函数式编程简介 函数式编程是和传统命令式编程区分的一种编程思想,"在函数式编程语言中,函数是第一类的对象,也就是说,函数 不依赖于任何其他的对象而可以独立存在,而在面向对象的语言中,函数 ( 方法 ) 是依附于对象的,属于对象的一部分.这一点决定了函数在函数式语言中的一些特别的性质,比如作为传出 / 传入参数,作为一个普通的变量等.[1]" 函数式编程思想的源头可以追溯到 20 世纪 30 年代,数学家阿隆左 . 丘奇在进行一项关于问题的可计算性的研究,也就是后来的 lambda

在JavaScript函数式编程里使用Map和Reduce方法

所有人都谈论道workflows支持ECMAScript6里出现的令人吃惊的新特性,因此我们很容易忘掉ECMAScript5带给我们一些很棒的工具方法来支持在JavaScript里进行函数编程,这些工具方法我们现在可以使用了.在这些函数方法里主要的是基于JavaScript 数组对象的map()方法和reduce()方法. 如果你如今还没有使用map()和reduce()方法,那么现在是时候开始使用了.如今绝大部分的JavaScript开发平台都与生俱来的支持ECMAScript5.使用Map方

javascript函数式编程简单介绍

函数式编程在前端已经成为了一个热门的话题,近几年很多的应用程序代码库里大量使用着函数式编程思想.这里对JavaSctipt中的函数式编程做一个简单介绍. 什么是函数式编程 函数式编程是一种编程范式,主要是利用函数把运算过程封装起来,通过组合各种函数来计算结果.函数式编程意味着开发者可以在更短的时间内编写具有更少错误的代码. 函数式编程的简单例子 假设要把一个字符串转换成每个单词首字母大写,可以这样来实现: var string = 'i do like yanggb'; var result =

JavaScript函数式编程(0):函数基础 arguments、this、apply()、call()

1 函数参数 函数的实参和形参个数可以不等,之所以会这样,原因是 ECMAScript 中的参数在内部是用一个数组来表示的.函数接收到的始终都是这个数组,而不关心数组中包含哪些参数(如果有参数的话).如果实参个数大于形参个数,多余的实参不传递值,但是在arguments中可以访问:如果形参个数大于实参,没有传递值的实参将自动被赋予 undefined 值. 2 arguments和this 所有的函数调用都会传递两个隐式参数:arguments和this.实际上,在函数体内可以通过 argume

javaScript函数式编程-包含闭包、链式优化及柯里化

本文着重介绍个人理解的函数式编程. 函数式编程个人理解为:以函数为主要载体的编程方式. 好处: 语义更加清晰 可复用性高 可维护性好 作用域局限.副作用少 基本函数式编程: //实现数组中每个单词首字母大写 //一般写法 const arr = ['apple','orange','pear']; for(const i in arr) { const c = arr[i][0]; arr[i] = c.toUpperCase() + arr[i].slice(1); //slice()从已有的

javascript函数式编程一例分析

js像其他动态语言一样是可以写高阶函数的,所谓高阶函数是可以操作函数的函数.因为在js中函数是一个彻彻底底的对象,属于第一类公民,这提供了函数式编程的先决条件. 下面给出一个例子代码,出自一本js教程,功能是计算数组元素的平均值和标准差,先列出非函数式编程的一种写法: var data = [1,1,3,5,5]; var total = 0; for(var i = 0;i < data.length;i++) total += data[i]; var mean = tatal/data.l