JavaScript系列:JavaScript函数式编程

219 阅读3分钟

JavaScript并非函数式编程语言,但是可以在JavaScript中应用函数式编程技术。简单的来说,就是一种编程的风格。

纯函数

概念:

  1. 相同的输入,一定会产生相同的输出。
  2. 在函数的执行过程中,没有副作用的产生。

实例解析:

// 实例一
function foo(x) {
    return x + 1
}

这里的foo就是最简单的一个纯函数。 函数里面,只依赖于参数 x。 所以无论调用几次,在什么时候调用,都不会影响输出。

// 实例二
let x = 1
function foo(y) {
    return x + y
}

这里就不是纯函数了,因为它依赖于外部的变量 x,当外部的变量x发生变化的时候, 就会直接影响到输出。

foo(2) // 3
x = 4
foo(2) // 6

没有相同的输出,就不是纯函数。

// 实例三
function (x) {
    // 发送网络请求
    const res = fetch(x)
    return res
}

这里也不是纯函数,请求返回的数据,会随着外界的变化而变化的。

什么是副作用(side effect)呢?:

改变了外部的环境(变量, localStorage等等)

    function foo() {
        localStorage.setItem(xxx, xxx)
    }

这里也不是纯函数。

纯函数优点:

  1. 不会因为改变外部的变量而产生bug
  2. 函数业务逻辑单一

柯里化函数

柯里化函(currying): 是把接受多个参数的函数,变成接受单一参数的函数,并且返回接受剩余参数的新函数

只传递给函数一部分的参数来调用,让它返回新的函数去处理剩余的参数,这个过程就是柯里化

function add(x, y, z) {
  return x + y + z;
}

// 等价于
function add(x) {
  return function(y) {
    return function (z) {
      return x + y + z;
    }
  }
}

// 等价于
const add = x => y => z => x + y + z
// 箭头函数的return表达式的省略

当然并不是说,柯里化函数只接受一个参数,也可以接受多个。

function add(x, y) {
    return function(z) {
        return x + y + z
    }
}

实现柯里化函数

首先先了解一个知识点:

function add(x, y, z) {}
add.length  // 3

函数的length属性值就是函数接受参数的个数

/**
 * 
 * @param {() => any} fn: 接受的函数
 * @returns 返回一个柯里化函数
 */

function autoCurry(fn) {
  return function currying(...args1) {
    //判断传递参数,跟fn的所需要的参数的长度是否一致
    if (fn.length <= args1.length) {
      return fn.apply(this, args1)
    } else {
      // 递归继续执行,跟fn的参数个数相同的时候
      return function (...args2) {
        return currying.apply(this, [...args1, ...args2])
      }
    }
  }
}
// 使用
function add(x, y, z) {
    return x + z + y
}
const curryFn = autoCurry(add)

curryFn(1)(2)(3)  // 6
curryFn(1,2)(3)   // 6
curryFn(1)(2, 3)  // 6

柯里化函数优点

  1. 每个函数的职责单一,而不是把所有的代码逻辑全部写在一个函数里面
  2. 缓冲值,可以在下次的调用的时候,使用缓冲值。
funtion add(x, y) {
    return x + y 
}
const get1 = add(1) // get1函数中,就缓存了1的值
get1(2) // 实现 1 + 2
get1(3) // 实现 1 + 3

组合函数

组合函数: 是JavaScript开发中的一种函数的使用技巧。 针对多个函数,依次执行。

function add(x, y) {
    retutn x + y
}

function square(x) {
    return **x
}

const num = square(add(1, 2))  // 9

上面的调用过程,就是先执行add函数,然后执行square函数。

那么组合函数,就是用来处理这种情况的:先执行第一个函数,拿到返回的结果值,把结果值作为参数传递给第二个函数,依次类推...

const add = compose(add, square)(1,2)
// 1 和 2作为第一个函数的参数

实现compose函数

/**
 * 
 * @param  {...any} fns 函数
 * @returns 返回值:any
 */
function compose(...fns) {
  const len = fns.length
  for (let i = 0; i < len; i++) {
    if (typeof fns[i] !== 'function') {
      throw new TypeError('Expected a function ')
    }
  }
  return function (...args) {
    let res = null;
    for (let i = 0; i < len; i++) {
      if (i === 0) {
        res = len ? fns[i].apply(this, args) : args
      } else {
        res = fns[i].call(this, res)
      }
    }
    return res
  }
}