PeopleMoutainPeopleSea
PeopleMoutainPeopleSea

Reputation: 1512

Why modify Number.prototype.toString does not work?

In javascript, when using add sign (+) to concatenate a string and another variable, that variable will implicitly call its toString method if it is not a string. To verify this, I made a constructor called Apple.

function Apple(name) {
    this.name = name;
}

Apple.prototype.toString = function() {
    console.log('Apple.prototype.toString called.');
    return this.name;
};

var apple = new Apple('Thai apple');
var msg = apple + ' tastes good.'
console.log(msg)

It works as I expected: when calculating apple + ' tastes good', Apple.prototype.toString is called. Then I did a similar experiment on Number type.

Number.prototype.num2str = Number.prototype.toString;
Number.prototype.toString = function() {
  console.log('new Number.prototype.toString called.');
  return this.num2str();
}

var msg = 'num = ' + 123;
console.log(msg);

After running it, I noticed that Number.prototype.toString is not called. I'm confused. Why doesn't it work like the previous example?

Upvotes: 1

Views: 903

Answers (1)

Felix Kling
Felix Kling

Reputation: 816790

Why doesn't it work like the previous example?

toString is only called if the value is an object (but see below). 123 is not an object, it's a primitive (number) value. Converting primitive values to string values follows different rules. See §7.1.12 in the ES2016 spec, and §7.1.12.1 specifically numbers (too long to quote here) for how values are converted to strings.

Primitive values are not coerced to objects when the + operator is used.


However, even if you'd create a number object it wouldn't work. That's because toString is not actually called for number objects. Why is that?

When performing addition, the following steps happen:

[...]
5. Let lprim be ? ToPrimitive(lval).
6. Let rprim be ? ToPrimitive(rval).
[...]

Both values are converted to primitive values first before they are converted to a number or a string specifically (a primitive value can be a string, number, boolean, null or undefined).
Converting objects to primitive values this way will ultimately calls valueOf first. Only if the return value of that function is not a primitive value will toString be called.

Because new Number(123).valueOf() returns a primitive value, toString will not be called!

Here is your example adjusted for valueOf:

Number.prototype.valueOf = function() {
  console.log('new Number.prototype.valueOf called.');
  return 'something';
}

var msg = 'num = ' + new Number(123);
console.log(msg);


NB: toString and valueOf are actually outdated ways of converting objects to strings/numbers/primitives. Since ES6, the new is to call the @@toPrimitve method of the object. Only if that doesn't exist, toString or valueOf will be called (see §7.1.1 ToPrimitive).

Upvotes: 4

Related Questions