基础[JavaScript] TC39特性管道运算符的使用

221 阅读3分钟

什么是管道?

管道操作符是操作系统中常用的处理数据的方法,属于软件架构风格中的管道过滤器风格,原理是将前一个节点的输出作为下一个节点的输入,每个节点只针对输入进行处理。

目前现有的JavaScript语言针对可枚举类型实现了链式调用,或者将值传递给函数,实现类似效果,如a(b(c([]))),但这这种效果是基于函数嵌套实现的,如果有多个方法,则函数嵌套层级会变的更深,降低了代码的可读性提高了代码的维护难度。

针对链式调用的一个前提是必须指定一个对象,通过对象将各个函数串在一起,实现构建,这也是设计模式中的建造者模式。

那么为什么会出现管道运算模式呢?

目的是为了提高代码的可读性和流畅性,如果我们使用管道符号,就可以脱离上述的两种方式,实现不同函数之间仅仅关注自身的输入和输出,而无需关系代码的整体组织,将其交给管道符号,使其能够提升函数的灵活性,同时管道概念本身也是如此,在GNU Linux操作系统中,不同的命名通常可以使用管道符号进行联合使用,而每个命令也只是符合操作系统的标准输入输出,命令只针对自己需要的函数进行处理即可,通过管道运算符完成复杂逻辑处理。

同时管道运算也大大简化了临时变量的声明和使用。

@babel/plugin-proposal-pipeline-operator的配置

proposal有四种配置,分别是 hachfsharpminialsmart

  • hack hack管道,可使用多元函数(多参数函数)
  • fsharp 带 await 的F#管道,用于一元函数的调用(单参数函数)
  • minimal 最小F#样式管道
  • smart 智能混合管道,已经弃用

目前 hackfsharp都是比较重要的,fsharp是通过单参数来隐式接受传入的变量,更加符合传统意义上的管道模式,而hack则可以利用传出的对象的指定函数直接处理,更加灵活

配置使用环境

{
  "devDependencies": {
    "@babel/cli": "^7.23.4",
    "@babel/core": "^7.23.6",
    "@babel/preset-env": "^7.23.6",
    "@babel/plugin-proposal-pipeline-operator": "^7.23.3" // babel的TC39管道实现
  }
}
## babel配置
```json
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "node": "14"
        }
      }
    ]
  ],
  "plugins": [
    ["@babel/plugin-proposal-pipeline-operator", { "topicToken": "^^" ,"proposal":"hack"}] // 设置占位符为 ^^, 模式为 hack模式
  ]
}

测试代码

[1,2,3,4,5,6,7]
  .map(envar => envar)
  |> ^^.map(item=> item + 'pipeline')
  |> ^^.join()
  |> console.log(^^) // 1pipeline,2pipeline,3pipeline,4pipeline,5pipeline,6pipeline,7pipeline

经过babel编译后,代码变成了通过map的形式进行连接处理

"use strict";

var _ref, _ref2;
_ref2 = [1, 2, 3, 4, 5, 6, 7].map(envar => envar).map(item => item + 'pipeline').join(), (_ref = console.log(_ref2), console.log(_ref + 'stage'));

如果我们自定义函数进行处理,代码如下

function mixinTitle(str){
  return str.map(item=>item+'title')
}
[1,2,3,4,5,6,7]
  .map(envar => envar)
  |> mixinTitle(^^)
  |> mixinTitle(^^)
  |> mixinTitle(^^)
  |> mixinTitle(^^)
  |> mixinTitle(^^)
  |> ^^.join()
  |> console.log(^^)

最终经过babel编译后的代码生成如下形式,我们可以看到,babel最终会将自定义函数处理成嵌套格式,实现该效果

"use strict";

var _ref;
function mixinTitle(str) {
  return str.map(item => item + 'title');
}
_ref = mixinTitle(mixinTitle(mixinTitle(mixinTitle(mixinTitle([1, 2, 3, 4, 5, 6, 7].map(envar => envar)))))).join(), console.log(_ref);

如果使用f#的方式直接调用一元函数,则书写格式如下,每个函数块只接受一个参数

function mixinTitle(str){
  return str.map(item=>item+'title')
}
function join(arr) {
  return arr.join()
}
[1,2,3,4,5,6,7]
  .map(envar => envar)
  |> mixinTitle
  |> mixinTitle
  |> mixinTitle
  |> mixinTitle
  |> mixinTitle
  |> join
  |> console.log

经过babel编译后的代码格式如下

生成的逻辑代码包含了很多临时变量

"use strict";

var _ref, _ref2, _ref3, _ref4, _ref5, _ref6, _map;
function mixinTitle(str) {
  return str.map(item => item + 'title');
}
function join(arr) {
  return arr.join();
}
_ref = (_ref2 = (_ref3 = (_ref4 = (_ref5 = (_ref6 = (_map = [1, 2, 3, 4, 5, 6, 7].map(envar => envar), mixinTitle(_map)), mixinTitle(_ref6)), mixinTitle(_ref5)), mixinTitle(_ref4)), mixinTitle(_ref3)), join(_ref2)), console.log(_ref);