问题的背景
许多严格类型的语言(如 C++、C#、Python)允许在类级别显式描述运算符(例如 +、-、*、==)的重载。在 TypeScript 和 JavaScript 中,由于历史和架构原因,语言级别上缺乏运算符重载。
问题
当需要为复杂类型(例如复数或向量)添加算术或其他操作的支持时,无法更改标准运算符。这限制了表达能力,要求我们发明绕过的模式。
解决方案
在 TypeScript 中,通过专门命名的方法(例如 add、equals、multiply)来模拟运算符重载。通过为类提供这些方法,我们实现了类型安全的行为,尽管语法看起来没有 C++ 那么简洁。
代码示例:
class Vector { constructor(public x: number, public y: number) {} add(other: Vector): Vector { return new Vector(this.x + other.x, this.y + other.y); } equals(other: Vector): boolean { return this.x === other.x && this.y === other.y; } } const a = new Vector(1, 2); const b = new Vector(3, 4); const c = a.add(b); // Vector { x: 4, y: 6 } const eq = a.equals(b); // false
关键特点:
可以在 TypeScript 中重写"=="或"+"运算符的行为吗?
不可以,JavaScript(和 TypeScript)不允许直接重载用户对象的标准运算符。运算符严格按照 Object、Number 和其他原始类型的标准进行应用。任何此类“重写”的尝试都会导致不正确或意外的行为,或者直接不工作。
可以使用 valueOf 或 toString 来间接影响运算符吗?
在某些情况下,可以使用 valueOf(例如,进行简单的数值转换),但仅限于那些调用对象原始值的运算符。
class Box { constructor(private v: number) {} valueOf() { return this.v; } } const a = new Box(10); console.log(a + 5); // 15 — 有效,但对于复杂对象来说不是直观的
但是对于复杂结构和逻辑运算符,显式的方法更为合适。
TypeScript 或 ECMAScript 中是否有添加运算符重载的计划?
目前没有确认的计划:原则上,运算符重载与 JS 的设计相悖,因为它可能会显著改变标准对象的行为并导致代码的不稳定。
开发人员创建一个 Matrix 类,在其中实现 valueOf/toString 以插入数学表达式。与数字或字符串相加时,得到了不可预测的结果,无法进行严格的类型化。
优点:
缺点:
Vector 类实现了 add、multiply、equals 方法,并明确了输入和输出参数的类型。类的客户端明确使用相应的方法,语义不会丢失,并确保了严格的类型化。
优点:
缺点: