Javascript 新特性前瞻 —— Pipe Operator

1,485 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 1 天,点击查看活动详情

TC39

我们都知道 ECMAScript 的规范制定和推广有一个 ECMA 国际组织在负责,该组织结构庞大,其中第 39 号技术委员会主要负责跟进 ECMAScript 在实现和使用阶段存在的问题,或者接受新的功能或想法及早落地,以便在后续版本中修复或推进。

针对新功能来说,任何想法可以提出来,但只有 TC39 成员可以提交。提交之后审核流程大致如图:

1d6e4fb9580450805758275fbd9d9607c7777bc5.svg

  • Stage 0 - 设想(Strawman):只是一个想法,可能有 Babel插件。
  • Stage 1 - 建议(Proposal):这是值得跟进的。
  • Stage 2 - 草案(Draft):初始规范。
  • Stage 3 - 候选(Candidate):完成规范并在浏览器上初步实现。
  • Stage 4 - 完成(Finished):将添加到下一个年度版本发布中。

新的想法提交之后一般会经历以上几个阶段,具体提案进程可在 TC39 Proposal 里查看。

Pipe Operator

今天来了解一下管道操作符,目前该提案处于 Stage-2 草案阶段,有望加到后续版本规范中。

提案开篇讲到为何要进行这样的提报,是因为在 2020 年的调查统计“你觉得目前 JavaScript 中缺少什么能力?” 报告中,Pipe Operator 排名第四,群众呼声很高。可能是很多开发者都羡慕其他高级语言中都有这项能力,不管怎么说,我们来看看它是怎么定义和使用的。

定义和使用

管道操作符主要用来做链式操作,我们知道在 JavaScript 中链式操作的模式一般分为两种:

  • c(b(a(value)))
  • a(value).b().c() 这两种方式可读性差且不太好理解,目前管道操作符给出的推荐方案语法如下:
expression |> function

使用方式举例:

const greeting = name => `Hello ${name}`;
const name = 'World';

console.log(name |> greeting); // Hello World

如果要实现之前的链式调用效果,假设我们有三个计算的工具方法:

let addOne = x => x+1;
let multiplyTwo = x => x*2;
let divideByThree = x => x/3;

假设对一个数字进行操作,链式调用这三个方法:

let number = 9
let res = number |> divideByThree |> multiplyTwo |> addOne
console.log(res) // 7

输出的结果很好分析,表达式左边的输出作为右边的输入;即最开始的 number 为 9,给了 divideByThree 后得 3 ,然后 multiplyTwo 得 6,再最后加 1 结果为 7。其内部转化具体是这样的:

(_ref2 = 
    (_ref1 = 
        (_ref = number, divideByThree(_ref)), 
    multiplyTwo(_ref1)),
addOne(_ref2))

如果你想使用这个特性可以在 codepen(配置好babel) 或者 babel 平台try-it-out 菜单里尝试,babel 平台是提供了各个阶段的语法支持的: image.png

补充说明

也许你觉得上面的例子太过简单或者不够灵活,按照提案里的想法,它提供的语法应该还支持这样的写法:

value |> foo(%)
v => v |> % == null |> foo(%, 0)
value |> x=> `${x}`

可以看出来管道操作符是支持占位符匹配的,那么上面的例子理论上可以改造成:

let res = number |> divideByThree(%) |> multiplyTwo(%) |> addOne(%)

但在 babel 中测试发现它并没有按提案给予实现,它会直接报错,实际上 babel 的方式看起来就是把左边的输入作为右边的参数,而且默认右边是一个可执行的函数。比如这样的写法6 |> divideByThree() 在 babel 中直接是转化成 (_ref =6, divideByThree()(_ref)),和我们期望的效果是不一致的。

但其实我们也可以利用 babel 的实现做自己的改造,比如说拼接请求地址参数:

let iobj = {
    name: 'sam',
    age: 24
}
let reqParams = Object.keys(iobj).map(key => `${key}=${iobj[key]}`) |> (arr=>arr.join('&')) |> (str=>`?${str}`);

console.log(reqParams) // ?name=sam&age=24

可以看到第二步的时候借用 IIFE 技术把前一个表达式的结果作为参数传进去,得到用 & 链接的键值对字符串,最后拿到想要结果。

总结

管道操作符是 2020 年 8 月份提出来的,到现在也差不多一年半时间,目前处于 stage-2 也算进展比较快的,而且它当时还把其他提案(F# pipes)比下去了,未来可期吧。大家怎么看待管道操作符呢,欢迎评论区留言沟通。

以上,感谢阅读。