JavaScript函数式编程笔记

函数式编程理论基础

1.数学中的函数书写如下形式 f(x)=y。一个函数F,以x作为参数,并返回输出y。这很简单,但是包含几个关键点,函数必须总是接受一个参数,函数必须返回一个值,函数应该依据接收到的参数,而不是外部环境运行,对于给定x只会输出唯一的y,也就是纯函数。
2.函数式编程不是用函数来编程,也不是传统的面向过程编程。主旨在于将复杂的函数分解成简单的函数。运算过程尽量写成一系列嵌套的函数调用。
3.通俗写法function xx(){}区别开函数和方法。方法要与指定的对象绑定,函数可以直接调用。
4.函数式编程相对与计算机的历史而言是一个非常古老的概念。函数式编程的基础模型来源与lambda演算(x=>x*x),即他来源于数学,所谓函数式编程就是用数学的方式编程,即要抛弃类似if,else,for等这些概念,因为数学里面没有这些概念。
5.随着react高阶函数的火热,函数式编程的概念也逐步升温。

重要概念

1.函数是"一等公民"。
2.只用“表达式”,不用“语句”。
3.没有副作用。
4.不修改状态。
5.引用透明。

专业术语

1.纯函数:对于相同的输入,永远会得到相同的输出,并且没有任何可观察的副作用,也不依赖外部环境状态。举个例子:

var xs = [1,2,3,4,5]
//Array.slice是纯函数,因为他没有副作用,对于固定的输入,输出总是固定,
//splice就不是,因为他会改变原数组
xs.slice(0,3) 
xs.slice(0,3) 
xs.splice(0,3)
xs.splice(0,3)  

纯函数不仅可以有效降低系统的复杂度,还有很多特性,比如可缓存性,但缺点是纯函数内部扩展性较差

//不纯的
var min = 18
var checkage = age=> age > min;
//纯的
var checkage = age=> age > 18;

在不纯的版本中,chackage不仅取决与age还有外部依赖的变量min。
纯的checkage把18写在了函数内部,扩展性较差。
2.偏应用函数:传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数;偏函数之所以‘偏’,就在与其只能处理啊那些能与至少一个case语句匹配的输入,而不能处理所有可能的输入。

const partial = (f,...args)=>
            (...moreArgs)=>
           f(...args,...moreArgs)

3.函数的柯里化:柯里化通过偏应用函数实现。它是把一个多参数函数转换为一个嵌套一元函数的过程。传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。

var checkage = min => (age => age > min)
var checkage18  = checkage(18)
checkage18(20)

这里柯里化解决了纯函数硬编码的问题。即把18写在函数内部,我们其实并不想函数这么死板。

//柯里化之前
function add(x,y){
 return x + y;
}
add(1,2)//3
//柯里化之后
function addX(y){
  return function(x){
    return x + y;
  }
}
addX(2)(1)//3

函数的反柯里化code

Function.prototype.uncurring = function() {
    var self = this;
    return function() {
        var obj = Array.prototype.shift.call(arguments);
        return self.apply(obj,arguments);
    }
}
var push = Array.prototype.push.uncurring(),
obj = {}
push(obj,"first","two")
console.log(obj)

函数的柯里化code

const curry = (fn, arr = []) => (...args) =>
    (arg => (arg.length === fn.length ? fn(...arg) :
        curry(fn, arg)))([
            ...arr,
            ...args
        ])
let curryTest = curry((a,b,c,d) => a + b + c + d)
var num1 = curryTest(1,2,3)(4) 
var num2 = curryTest(1,2)(4)(3)
var num3 = curryTest(1,2)(3,4)
console.log(num1, num2, num3); // 10 ,10 ,10

柯里化优缺点:事实上柯里化上一种“预加载”函数的方法,通过传递较少的参数,得到一个已经记住了这些参数的新函数,某种意义上讲,这是一种对参数的“缓存”,是一个非常高效的便携函数的方法。
4.函数组合:纯函数以及如何把它柯里化写出的洋葱代码h(g(f(x))),为了解决函数嵌套的问题,我们需要用到“函数组合”。

const compose = (f, g) => (x => f(g(x)));
var first = arr => arr[0];
var reverse = arr => arr.reverse();
var last = compose(first, reverse);
console.log(last([1,2,3,4,5]));//5
//组合一个函数获取数组最后一项

更加专业的术语

高阶函数,尾调用优化PTC,容器,Functor,错误处理,Either,AP,IO,Monad
1.高阶函数:函数当参数,把传入的函数做一个封装,然后返回这个函数,达到更高程度的抽象。

//命令式
var add = function(a,b) {
   return a + b
}
function math(func,array) {
   return func(array[0], array[1])
}

math(add, [1,2])
  • 它是一等公民
  • 它以一个函数作为参数
  • 它以一个函数作为返回结果

2.尾调用优化:指函数内部的最后一个动作是函数调用。该调用的返回值,直接返回给函数。函数调用自身,称为递归。如果尾调用自身,就称为尾递归。递归需要保存大量的调用记录,很容易发生栈溢出错误,如果使用尾递归优化,将递归变为循环,那么只需要保存一个调用记录,这样就不会发生栈溢出错误了。
3.范畴与容器

  • 我们可以把“范畴”想象成是一个容器,里面包含两样对象。值(value),值的变形关系,也就是函数。
  • 范畴论使用函数,表达范畴之间的关系。
  • 伴随着范畴论的发展,就发展出一整套函数的运算方法。这套方法起初只用与数学运算,后来有人将它在计算机上实现了,就变成了今天的“函数式编程”。
  • 本质上,函数式编程只是范畴论的运算方法,跟数理逻辑,微积分等都是一类,都是数学方法,只是碰巧他能用来写程序。为什么函数式编程要求函数必须是纯的,不能有副作用?因为它是一种数学运算,原始目的就是求值,不做其他事情,否则就无法满足函数运算的法则了。

4.Functor

  • 函数不仅可以用于同一个范畴之间值的转换,还可以用于将一个范畴转成另一个范畴,这就涉及到了函子(Functor)
  • 函子是函数式编程里面最重要的数据类型,也是基本的运算单位和功能单位。它首先是一种范畴,也就是说,是一个容器,包含了值和变形关系。比较特殊的是,他的变形关系可以依次作用于每一个值,将当前容器变成另一个容器。

更新中。。。

你可能感兴趣的:(JavaScript函数式编程笔记)