【问题标题】:Sub-class of Number persistently gets converted back to built-in Number Object/FunctionNumber 的子类持续转换回内置的 Number 对象/函数
【发布时间】:2021-09-17 11:59:19
【问题描述】:

为了不污染或扩展原型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 }
}

【问题讨论】:

  • 不像你的标题说的那样,它不会被转换回Number。应用 += 运算符会给出 primitive 值。 num.constructor 表达式临时将原始 num 包装成 Number。
  • @trincot 我明白你的意思。 num instanceof Number 返回错误。我可以添加一个math() 方法,它接受当前值的参数,计算值,然后将新值作为新的MyNum 实例返回。
  • “类似于 Number 和 BigInt 类型允许这些”:它们不允许。 (Number(1) + Number(2)) instanceof Number 是假的。同样在那里,结果是一个“数字”原语(或分别为“bigint”原语)。
  • @trincot 对我来说这些数字实际上是数字“原语”是有道理的,但是这些数字原语为什么/如何从Number.prototype 继承方法?例如,如果我定义Number.prototype.round,它可用于数字原语。
  • 这是因为当您尝试访问属性时,原语被包装到本机对象中。这是您无法覆盖的行为。

标签: javascript class javascript-objects prototype prototype-programming


【解决方案1】:

如果你愿意

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) }

【讨论】:

  • 我打算再等几天左右,然后再选择一个答案,看看人们可能会提出什么其他解决方法。感谢您的详细回复!
  • 对我来说,这些数字实际上是数字“原语”,正如我在原始问题的评论中提到的 trincot 一样,但是这些数字原语为什么/如何/如何从Number.prototype 继承方法?例如,如果我定义Number.prototype.round,它可以与数字原语一起使用。
猜你喜欢
  • 1970-01-01
  • 2017-10-17
  • 1970-01-01
  • 2016-03-10
  • 1970-01-01
  • 2015-03-22
  • 1970-01-01
  • 1970-01-01
  • 2021-04-28
相关资源
最近更新 更多