特点

  • 声明式编程,关注每一步的输入输出
  • 若干纯函数的组合,集中处理的副作用
  • 想要了解整体代码逻辑,只需去阅读函数组合在语意上做了什么事情

    纯函数特点

  • 幂等性

  • 引用透明,函数调用能被函数输出值直接替代
  • 结果可预测
  • 方便测试

    纯函数组合需要满足的条件

    组合指的是代码以明确,明显和可读的方式描述数据流,如果函数类型 (shape) 相匹配,函数就能比较容易组合起来
    函数类型由输入输出值的个数决定

  • 一元函数(unary function):单输入,单输出

  • 二元函数(binary function):两个输入,单个输出

函数具备相同的类型时是可互换的(interchangeable)

保证函数类型一致的方法(改变函数参数个数)

偏函数

  1. function partial(fn,...presetArgs) {
  2. return function partiallyApplied(...laterArgs){
  3. return fn( ...presetArgs, ...laterArgs );
  4. };
  5. }

柯理化

  1. function curry(fn,arity = fn.length) {
  2. return (function nextCurried(prevArgs) {
  3. return function curried(nextArg) {
  4. var args = [ ...prevArgs, nextArg ];
  5. if (args.length >= arity) {
  6. return fn( ...args );
  7. }
  8. else {
  9. return nextCurried( args );
  10. }
  11. };
  12. })( [] );
  13. }

常用组合及典型例子

compose函数

将一个函数的输出立即传入另外一个函数当作输入
函数从右向左执行;

pipe函数

函数从左向右执行;

function compose(...args) {
  return pipe(...fns.reverse())
}
function pipe(...fns) { 
  return function piped(v){
    for(let fn of fns){
      v = fn(v)
    }
    return v
}}

递归函数(recursion)

  • 基线条件(base case):函数不再调用自己的条件
  • 递归条件(recursive case): 函数调用自己的条件
  • 易懂消耗内存

内存优化方案

  • tail calls,可能大家听说最多的递归优化方案就是尾调用,不过尾调用需要系统,语言,编译器,运行环境等等都支持才行,目前只有 Safari 浏览器支持,需要在 strict 模式,且递归直接返回函数时才生效;
  • CPS:一种欺骗性的突破栈内存限制的方法;
  • trampolines:一种真实有效的节省内存的方式;

    reducer

    任何有两个参数的函数从某种意义上都可以看作 reducer

    transduction

    monad

    其他

    参数的表示

    parameters 的个数可以通过 fn.length 获取,其在定义时决定
    arguments.length 属性则在运行时决定

    函数名很重要:

    函数名能出现在调用栈中,排查bug
    在需要自引用的地方,函数名必不可少,比如递归或者事件处理函数

    副作用

  • I/O (console,files,etc)

  • Database Storage
  • Network Calls
  • DOM
  • TimeStamps
  • Random Numbers
  • CPU Heat
  • CPU Time Delay:对系统有影响