领先的免费Web技术教程,涵盖HTML到ASP.NET

网站首页 > 知识剖析 正文

RxJS——自制 map 操作符和静态版 pipe 函数

nixiaole 2024-11-14 18:37:00 知识剖析 18 ℃


现在我们知道如何创建一个自定义操作符了。为了巩固学到的知识,自己实现一下使用率最高的 map 操作符。我们知道 map 操作符,或者说 map 函数在使用时是要传入一个函数作为参数的,因此我们会得到如下的函数签名:

fn -> source -> source

对照之前 add 操作符的实现,我们有了如下的代码骨架:

Bash
import { Subscriber } from 'rxjs';
class MapSubscriber extends Subscriber {}

const map = fn => source => {
  return source.lift({
    call(sub, source) {
      source.subscribe(new MapSubscriber(sub, fn));
    }
  });
};

现在关键的部分就是如何实现 MapSubscriber 自定义类。继续照抄之前的代码:

Bash
class MapSubscriber extends Subscriber {
  constructor(subscriber, fn) {
    super(subscriber)
    this.fn = fn
  }
  _next(value) {
    this.destination.next(this.fn(value));
  }
}

其实非常简单,把之前 num 参数换成 fn 参数,只是在最后的 next 中要思考一下,对数据源中的数据做什么操作。在 add 操作符中,我们是对原始值进行加法操作,加的值为我们传入的参数 num。然而在 map 操作符中,我们传入的参数是一个函数,这个函数的目的就是输入一个值,输出另一个值,输出什么,怎么得到新值,都由使用者来决定。因此,我们的实现如上面的代码所示,把数据源中的数据当做参数传给这个函数,函数运行后的结果作为输出继续传给下面的操作符。

一个非官方版的 map 操作符就完成了。

之前的文章我们说过,有两种自定义操作符的方法。一种是 lift 函数,一种是直接在 source 上使用 pipe 函数。其实还有第三种方式,那就是静态版 pipe 函数。我们可以从 rxjs 中导入 pipe函数实现之前的 add 操作符,代码如下:

const add = num => pipe(map(v => v + num));

pipe 的作用就是组合操作符,作为练习,我们同样可以尝试自己来实现以下静态版 pipe 函数。根据 pipe 的使用方式,我们得到以下信息:pipe 可以接收任意多个参数,每个参数都是一个函数,这个函数的签名为 source -> sourcepipe 函数的返回值也是一个函数,签名也是 source -> source。因此 pipe 函数的签名为:

fns -> source -> source

有了签名,如何实现呢?试着写一下:

const pipe = (...fns) => source => ... // 省略号的地方怎么写呢?

我们知道 fns 是一堆操作符,也就是一堆 source -> source。伪代码是这样的:

pipe(
  source -> source,
  source -> source,
  ...
)

pipe 最终返回的还是个 source -> source。那么其实是不是就是把这些操作符串起来,source从第一个操作符出来传给第二个操作,以此类推,直到最后输出的那个 source。换句话说,每一个操作符的输入都是上一个操作符的输出,变成了这样:

source -> source -> source -> source

熟悉 redux 的朋友肯定觉得眼熟,这不就是一个 reduce 的过程。是的,我们就是要用 reduce函数,因为恰好 fns 就是个数组,实现如下:

const pipe = (...fns) => source => fns.reduce((acc, fn)=> fn(acc), source);

reduce 函数就不讲了,主要看里面的聚合参数怎么写。fn 是操作符函数,acc 是累计值,它的初始值为 source。聚合函数的返回值为 fn(acc) 作为下次运行的累计值。其实这里叫累计值不太合适,其实是用每次运行后的结果覆盖了之前的结果。因为 fn(acc) 的结果还是个 source,恰好可以作为下个操作符运行时的输入参数,这样运行下去直到 fns 耗尽。最后的返回值肯定还是个 source,满足了 pipe 的签名需求。

今天就到这里了,如有任何问题,请添加微信公众号“读一读我”。

最近发表
标签列表