起因
JavaScript 本身目前并不支持操作符重载这种功能,很多时候如果需要处理不是数字的话很麻烦。或许你会封装成函数处理,但类似这样的代码却不是很直观。
const object = div(add(a, b), sub(c, add(d, mul(e, f))))
如果支持操作符重载能够像下面这样多好
const object = (a + b) / (c - (d + e * f))
方案
-
当然可以通过 Babel 插件在编译时修改源码实现,npm 中也存在一些这样的插件。但这样的代码仅能够在编译后运行,而且这样的代码在 TypeScript 中会直接报错。
-
后来我就想到可以通过 带标签的模板字符串 来模拟实现。
原理
const calc = (strings, ...arg) => {
console.log(strings) // ['', ' + ', '']
console.log(arg) // [a, b]
}
const object = calc`${a} + ${b}`
带标签的模板字符串其实将模版字符串拆分传递给了一个函数,这时就可以用函数解析模板字符串内容。
第一个参数包含一个字符串值的数组,其余的参数与表达式相关。这样只需遍历 strings 找到对应位置上的操作符、参数,将数据进行对应处理就完成了。
一个简单的字符串例子
当然上面例子仅仅是按照顺序计算的。但按照习惯,操作符或许还应该具有一定的优先级,而且还要实现分组操作符等功能
封装
根据原理和需求,我将核心代码封装开源在 tagged-operator,对实现感兴趣可以看看源码。npm 包 tagged-operator 已经发布,也可以安装尝试一下。
优势
- 这种方式相比调用函数看起来会更直观些
- 这里操作符其实就是普通字符串,理论上可以用任何符号、文字等作为操作符
- 原生支持,而且理论上经过 Babel 编译最低可以运行在 ES2015 以上
- 支持在 TypeScript 中使用,能够进行统一的类型限制
劣势
- 第一见这种代码可能比较难以理解,如果滥用一些奇怪的符号可能更加让人摸不到头脑
- 写法依旧有些笨重
- 类型支持不能像封装成函数那样限制每个参数类型,仅能够统一每个参数类型
不知道是否还有其它的方式实现,也可以交流下