ES 新语法 (stage1) :管道操作符 `|>`

在函数式编程中,经常遇到需要 链式调用函数 (chaining function calls) 的情况,例如 Express 、Koa 、Redux 的中间件。在 MDN 文档上有一个例子:

const double = (n) => n * 2;
const increment = (n) => n + 1;
double(increment(double(double(5)))); // 42

Compose function

很明显上面这样写就出现了深度嵌套的情形,不利于后期维护。在这个基础上,出现了一个叫 compose-function 的第三方模块。

https://www.npmjs.com/package/compose-function

使用 compose-function 很大程度上可以避免深度嵌套的情形,例如需要按顺序调用下面几个函数,利用 compose-function 就可以这样写:

import compose from 'compose-function'

class Person {
	constructor() {
		this.name = "";
		this.age = "";
	}
	setName = function(name) {
		this.name = name;
	}
	setAge = function(age) {
		this.age = age;
	}
}

function nameMiddleWare(person) {
	person.setName("dby");
	return person; // 中间件在执行完操作后,将 person 实例继续传递下去
}

function ageMiddleWare(person) {
	person.setAge(13);
	return person; // 中间件在执行完操作后,将 person 实例继续传递下去
}

function loggerMiddleWare(person) {
	console.log(`My name is ${person.name}, age is ${person.age}`);
	return person; // 中间件在执行完操作后,将 person 实例继续传递下去
}

const middlewares = [
	nameMiddleWare,
	ageMiddleWare,
	loggerMiddleWare
].reverse();

const composeFn = compose(...middlewares);
composeFn(new Person());

使用 reduce 实现 Compose Function

分析了一下上面的逻辑,首先调用 middlewares 里面第一个函数并传递初始值,拿到返回值后继续传递给第二个函数,以此类推。这个逻辑可以通过数组的 reduce 方法实现:

function compose<T>(...middlewares: ((arg: T) => T)[]): (initValue: T) => T {
	return (initValue) => {
		return middlewares.reduce((accu, cur) => {
			return cur(accu);
		}, initValue)
	}
}

function increment(n: number) {
	return n + 1;
}

function double(n: number) {
	return n * 2;
}

const middlewares = [
	double,
	double,
	increment,
	double
]

const composeFn = compose<number>(...middlewares);
composeFn(5); // 42

管道操作符

此外,ES 在语言层面上也加了一个特性,也就是 管道操作符 (pipeline operator) 。使用管道操作符就可以简化 compose function 的用法,语法如下:

expression |> function

管道运算符左边接收一个表达式,右边接收一个函数。管道运算符可以将表达式的值作为唯一参数通过管道传递给函数,允许以可读的方式创建函数的链式调用,其本质是语法糖。下面两种写法是等效的:

let url = "%21" |> decodeURI;
let url = decodeURI("%21");

有了这个之后,上面那个例子就可以这样写:

class Person {
	constructor() {
		this.name = "";
		this.age = "";
	}
	setName = function(name) {
		this.name = name;
	}
	setAge = function(age) {
		this.age = age;
	}
}

function nameMiddleWare(person) {
	person.setName("dby");
	return person; // 中间件在执行完操作后,将 person 实例继续传递下去
}

function ageMiddleWare(person) {
	person.setAge(13);
	return person; // 中间件在执行完操作后,将 person 实例继续传递下去
}

function loggerMiddleWare(person) {
	console.log(`My name is ${person.name}, age is ${person.age}`);
	return person; // 中间件在执行完操作后,将 person 实例继续传递下去
}

// 因为中间件每次调用之后都会返回传入的实例,因此可以这样链式调用
new Person() |> nameMiddleWare |> ageMiddleWare |> loggerMiddleWare

但是管道操作符作为 experimental feature,还在 stage 1 阶段,现在所有的浏览器都不兼容:

在这里插入图片描述

如果要使用这种语法特性,可以在 Nodejs 环境下,启用 @babel/plugin-proposal-pipeline-operator 插件:

https://babeljs.io/docs/en/babel-plugin-proposal-pipeline-operator

参考:
compose-function - npm
函数式编程中的compose
Pipeline operator (|>) - MDN
@babel/plugin-proposal-pipeline-operator - babel

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值