thecoop
thecoop

Reputation: 46128

Inheriting javascript Number to change toString

I'm trying to inherit from Number to override the toString method to output fixed decimal points when toString is called:

function FixedNumber(value) {
    Number.call(this, value);
}

util.inherits(FixedNumber, Number);

FixedNumber.prototype.toString = function() {
    return this.toFixed(3);
}

var n = new FixedNumber(5);
var s = n.toString();

Unfortunately, this doesn't work. I get the following exception:

TypeError: Number.prototype.valueOf is not generic
  at FixedNumber.valueOf (native)
  at FixedNumber.toFixed (native)
  at FixedNumber.toString (repl:2:13)
  at repl:1:3
  at REPLServer.self.eval (repl.js:110:21)
  at repl.js:249:20
  at REPLServer.self.eval (repl.js:122:7)
  at Interface.<anonymous> (repl.js:239:12)
  at Interface.EventEmitter.emit (events.js:95:17)
  at Interface._onLine (readline.js:202:10)

What have I done wrong, and how do I do what I want?

Upvotes: 2

Views: 400

Answers (1)

Qantas 94 Heavy
Qantas 94 Heavy

Reputation: 16020

When Number is called as a function (as what happens when you use call on it), it does not act like an "ordinary" constructor -- instead, it simply converts the argument to a number.

15.7.1.1 Number ( [ value ] )

Returns a Number value (not a Number object) computed by ToNumber(value) if value was supplied, else returns +0.

Other than that, it does nothing. What you're essentially returning is a plain object with a prototype based on Number.prototype -- the internal property that stores the actual number is not set.

Since the object that's returned is not a Number object, it fails because Number.prototype.toString is not generic, which means that it can only be used on actual Number objects or primitives. Even if it didn't throw an error, it'd probably return "NaN" as that's the default if not set.

There's really no "clean" way of doing this right now, though this will be possible with ES6 by subclassing native constructors.

Though this is sort of a hack and rather messy, you could do something like:

function FixedNumber(num) {
  this.toString = function () {
    return num.toFixed(3);
  };

  // implement the rest of Number.prototype as instance methods
}

// make instanceof return true
FixedNumber.prototype = Object.create(Number.prototype);

It's not really feasible to place these methods on the prototype, as there is no access to the actual number stored.

Upvotes: 4

Related Questions