开发自己的前端工具库(二):函数式编程

栏目: 编程语言 · 发布时间: 6年前

内容简介:本系列文章将通过自己的一个开发工具库的实战经验(踩过的坑)教大家如何开发属于自己的一个工具库,在这里你可以学到Git的使用规范,基础项目的搭建,代码编写的规范,函数式编程思想,TypeScript实战,单元测试,编写文档和发布NPM包等等知识。阅读文章你可能需要以下基础知识:

本系列文章将通过自己的一个开发 工具 库的实战经验(踩过的坑)教大家如何开发属于自己的一个工具库,在这里你可以学到Git的使用规范,基础项目的搭建,代码编写的规范,函数式编程思想,TypeScript实战,单元测试,编写文档和发布NPM包等等知识。

阅读文章你可能需要以下基础知识:

项目源码

Windlike-Utils

系列目录

  1. 开发自己的工具库(一):项目搭建

为什么要用函数式编程

因为函数式编程不会改变外部的变量,且对固定输入有唯一输出,这样我们可以不管函数内部的具体实现去使用它,而且可以很方便地通过组合多个函数而成我们想要的那个函数,更接近自然语言的表达。

比如我们要实现一个 y=f(x)=2*x+1 的函数,通常我们会这么写:

function f(x) {
    return 2*x + 1;
}

f(1);  // 3
复制代码

而函数式编程则是将他们拆分为几个小函数,再组装起来使用:

function double(x) {
    return 2*x;
}

function plusOne(x) {
    return x + 1;
}

plusOne(double(1));  // 3

// 或者还有更好一点的写法,这里暂未实现,
// 这里只是写下他们的调用方法,具体下面的文会讲到
const doubleThenPlusOne = compose(plusOne, double);
doubleThenPlusOne(1);
复制代码

纯函数

开发自己的前端工具库(二):函数式编程
  • 不可变性(immutable) 即对输入的实参及外部变量不能进行改变,没有副作用,以保证函数是“干净”的。
  • 唯一性 对每个固定输入的参数,都有唯一对应的输出结果,有点类似于数学里的 y=f(x) ,当输入的 x 不变,输出的 y 也不会改变

这是一个栗子:

const array = [1, 9, 9, 6];

// slice是纯函数,因为它不会改变原数组,且对固定的输入有唯一的输出
array.slice(1, 2);  // [9, 9]
array.slice(1, 2);  // [9, 9]

// splice不是纯函数,它即改变原数组,且对固定输入,输出的结果也不同
array.splice(0, 1);  // [9 ,9 ,6]
array.splice(0, 1);  // [9 ,6]
复制代码

柯里化(Currying)

柯里化就是传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。 我们上面实现了一个加一的函数,但当我们又需要一个加二的函数,又重新去写代码实现它的话效率是很低的,所以我们就需要柯里化,我们设想一下可不可以这样呢:

const plusOne = add(1);
const plusTwo = add(2);

plusOne(1);  // 2
plusTwo(2);  // 4
复制代码

这样我们就可以很容易地得到想要的函数,下面是 add 函数的实现:

function add(a) {
    return function(b) {
        return a + b;
    }
}
复制代码

虽然基本满足我们现在的需求,但感觉还是不太方便,如果我们要实现三个或多个数的相加我们可能得这样写:

function add(a) {
    return function(b) {
        return function(c) {
            return a + b + c;
        }
    }
}
复制代码

于是我们再设想一种更方便的方法:

function add(a, b, c) {
    return a + b + c;
}

const curryAdd = curry(add);
const plusOne = curryAdd(1);
const plusOneAndTwo - curryAdd(1, 2);

plusOne(2, 3);  // 6
plusOneAndTwo(3);  // 6
curryAdd(1)(2, 3);  // 6
curryAdd(1)(2)(3);  // 6
复制代码

这样我们就可以自由产生需要参数不同的函数啦,下面是 curry 的实现方法(有兴趣的同学可以先思考下再看):

function curry<Return>(fn: Function): CurryFunction<Return> {
    // 记录传进来的函数总共需要多少个参数
    let paramsLength: number = fn.length;

    function closure(params: any[]): CurryFunction<Return> {

      let wrapper: CurryFunction<Return> = function (...newParams: any[]) {
        // 将所有的参数取出
        let allParams = [...params, ...newParams];

        if (allParams.length < paramsLength) {
          // 如果参数数量还不够则返回新的函数
          return closure(allParams);
        } else {
          // 否则返回结果
          return fn.apply(null, allParams);
        }
      };

      return wrapper;
    }

    return closure([]);
  }
复制代码

可能有些不太好理解,一时看不懂的同学可以先跳过这里看下面~

这里是 源码 ,及 头文件定义

另外也可以用原生的 bind 函数来实现柯里化:

const plusOne = add.bind(null, 1);

plusOne(2, 3);
复制代码

函数组合(Compose)

函数组合就是把多个不同的函数组合成一个新的函数。

比如这样:

// 将函数从右往左组合
const doubleThenPlusOne = compose(plusOne, double);

// 1*2 + 1
doubleThenPlusOne(1);  // 3
复制代码
function compose<Return>(...fn: any[]): (...params: any[]) => Return {
    return (...params: any[]): Return => {
      let i = fn.length - 1;
      let result = fn[i].apply(null, params);

      while (--i >= 0) {
        result = fn[i](result);
      }

      return result;
    };
  }
复制代码

这里是 源码 ,及 头文件定义

延迟输出

有时候这个世界并不是那么美好的,并不是所有的代码都是那么“干净”的,比如I/O操作和DOM操作这些等待,因为这些操作都对外部有依赖,会对外部有影响。这时候就需要用延迟输出来保证我们的函数是“干净”的,例如下面实现的这个 random 函数:

function random(min: number = 0, max: number, float: boolean): () => number {
    return (): number => {
      if (min > max) {
        [min, max] = [max, min];
      }
      if (float || min % 1 || max % 1) {
        return min + Math.random() * (max - min);
      }

      return min + Math.floor(Math.random() * (max - min + 1));
    };
  }
复制代码

对于固定的输入,它总返回的是产生符合条件的随机数的函数,这样我们就通过“拖延症”来让我们的代码保持“干净”啦,是不是很机智呢!这样做的好处还有它通过闭包机制把参数都记住,缓存起来,下次可以不用重复传同样的参数:

const createRandomNumber = random(1, 100, false);

createRandomNumber();
createRandomNumber();  // 可以多次重复调用产生1到100随机数
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Node.js实战

Node.js实战

[美] Mike Cantelon、[美] TJ Holowaychuk、[美] Nathan Rajlich / 吴海星 / 人民邮电出版社 / 2014-5 / 69.00元

服务器端JavaScript?没错。Node.js是一个JavaScript服务器,支持可伸缩的高性能Web应用。借助异步I/O,这个服务器可以同时做很多事情,能满足聊天、游戏和实时统计等应用的需求。并且既然是JavaScript,那你就可以全栈使用一种语言。 本书向读者展示了如何构建产品级应用,对关键概念的介绍清晰明了,贴近实际的例子,涵盖从安装到部署的各个环节,是一部讲解与实践并重的优秀......一起来看看 《Node.js实战》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器