「这是我参与2022首次更文挑战的第35天,活动详情查看:2022首次更文挑战」
这一节来看 pipe 方法,pipe 是什么我们肯定都不陌生了,在前面已经见过无数次了,最新版本的 Rxjs 已经不再支持链式调用操作符了,想使用操作符不可避免的需要用到 pipe。
pipe 接收多个操作符作为参数,每个操作符进行一次处理,然后处理结果交给下一个操作符,如果把每个操作符看做一个函数,实际上对应的流程是这样的:a(b(c(data))),pipe 是从内向外的,这里是先交给 c 再到 b 再到 a,因此这里使用 pipe 应该是 pipe(a(), b(), c())。
上面提到的这种把前一个结果交给后一个处理者的机制成为管道,管道在程序中非常常见,最简单的就是在 shell 中:
cat "somefile.txt" | echo
这里先执行 cat 然后把 cat 的结果交给 echo,这个 | 就是一个管道操作。
在 js 中也有管道运算符,不过这种运算符目前还处于 stage 2 阶段,可以关注这个提案详细了解相关知识,目前管道操作符的用法大概是这样的,我们现在有一个原始的函数:
const y = h(g(f(x)))
转化为管道调用:
const y = x |> f(%) |> g(%) |> h(%)
这里的 |> 就是管道语法,使用 % 作为参数占位符。在业内管道运算其实有两种风格:Microsoft 的 F# 和 Facebook 的 Hack,js 这里借鉴的是 Hack 风格的处理方式,这种处理的特点就是需要有占位符。从语法的简洁性来说 F# 其实更优,它可以更好的结合 pointfree 风格,省略参数,更符合函数式编程的理念,但是由于 js 语法层面的限制,不能原生支持柯里化,目前使用 F# 风格会存在各种问题,因此目前采用的是 Hack 风格,未来会如何发展还不能够确定,最终语法需要等待提案 stable 后才能使用,到那时 Rxjs 也可以基于管道操作写出更简洁的程序。
在引入 pipe 之前,Rxjs 是使用链式调用的方式来连接操作符的,链式调用写起来很容易,类似 jQuery 一样,比较受欢迎,但是这种写法有一个很致命的问题,由于全部函数都返回 this,相当于都有全局的依赖,这样很难通过 tree shaking 的手段进行优化,而大部分时候我们只会用到部分操作符,全量引入不符合预期,因此最近几个版本 Rxjs 已经逐步弃用了链式调用的写法,也因此我们可以看到一部分操作符出现重命名的现象(如 do 改为 tap,catch 改为 catchError 等),这是因为链式调用时是对象方法,不会与 js 关键字冲突,而通过 pipe 引入的是一个独立的函数,这样是不允许与关键字重名的。
关于 pipe 与链式调用这里就不过多介绍了,pipe 背后涉及到的是函数式编程的一些理念,想要深入了解还需要知道 pipe 和 compose 的区别,这里不做展开了。