问题描述
为了不污染或扩展原型 Number
对象,我试图创建一个扩展 Number
的类。但是,当我这样做然后尝试向其中添加任何内容甚至将其添加到自身时,它会被转换回一个数字。
我了解发生这种情况是因为它采用数字 valueOf
属性值并将该值返回到另一个数字,但我希望该值始终保留其子类,而无需创建其他方法,例如:
.add(x)
.sub(x)
.mul(x)
.div(x)
有没有办法实现这一点,类似于 Number
和 BigInt
类型如何允许这些类型的计算?我可以(但不希望)扩展内置 Number 对象以支持我的自定义方法,但是现在我每次尝试使用它们时,它们都会丢失所有方法。
例如:
let num = 5;
console.log(num.constructor); // Number
num += 2;
console.log(num); // 7
console.log(num.constructor); // still Number
但是,与我的扩展班一起工作:
class MyNum extends Number {
constructor (num = 0) {
super(num);
}
get isEven() { return this.isInt && this % 2 === 0 }
get isOdd() { return this.isInt && this % 2 !== 0 }
}
let num = new MyNum(5);
console.log(num.constructor); // MyNum
num += 2;
console.log(num); // 7
console.log(num.constructor); // back to Number
此外,如果这里唯一的解决方案是添加像 .add(x)
这样的方法,我是否需要在完成像 .add(x)
这样的计算时重新创建一个新的类实例,或者是否有另一种更原生的方法来实现?添加到现有对象的值。
如果以下是唯一的解决方法,我可能会坚持扩展内置的 Number
对象:
class MyNum extends Number {
constructor (num = 0) {
super(num);
}
add(x) { return new MyNum(this.valueOf() + x) }
sub(x) { return new MyNum(this.valueOf() - x) }
mul(x) { return new MyNum(this.valueOf() * x) }
div(x) { return new MyNum(this.valueOf() / x) }
mod(x) { return new MyNum(this.valueOf() % x) }
get isEven() { return this.isInt && this % 2 === 0 }
get isOdd() { return this.isInt && this % 2 !== 0 }
}
解决方法
如果你愿意
num += 2;
导致 num
之前是 MyNum
,之后是 MyNum
,恐怕这是不可能的。正如 the specification 所说,所有值都将转换为基元 (valueOf
,toString
),之后基元要么连接在一起,要么相加 - 并且基元的连接或相加将导致以普通原语作为结果,可以是字符串、数字或 BigInt。
添加新的显式方法(如 .add
)是唯一真正的选择。
数字对象的值是 in an internal slot,它是不可更改的(无论如何您都不想更改,因为突变可能会令人困惑)。我认为没有比 add(x) { return new MyNum(this.valueOf() + x) }
等更好的方法。但是您可以通过删除 valueOf
调用稍微清理您的方法,不需要它们。
add(x) { return new MyNum(this + x) }
sub(x) { return new MyNum(this - x) }