How can I override the behavior of valueOf() as a prototype of the Number in JavaScript?

144 Views Asked by At

I was given a task to understand the .toString and .valueOf methods, who calls them, where they are used, how they work, how they are called and how to change calling sequence.

I am getting an error when I trying to override this:

Number.prototype.toString = function () {
  return "This is the number: " + this
};

Number.prototype.valueOf = function () {
  //console.log(this)
  return this;
}

console.log(new Number(33).toString());
console.log(new Number(34).valueOf())

Js returns an error RangeError: Maximum call stack size exceeded, how can I override value of to return, for example, string - 'The number is ${this}'

I've found out that everything is working if I remove console.log(new Number(33).toString());

I have tried to console.log this and got such output: Code output

2

There are 2 best solutions below

13
On BEST ANSWER

I really do not want to give this advice, and do not know why you wouldn't declare a sub Class that has its prototype set to Object.create(Number.prototype) but if you want to do it on all Numbers, than you can go with a monkey patch:

Number.prototype.toString = (function(f){return function(){return `whatever ${f.call(this)}`}})(Number.prototype.toString)

Number.prototype.valueOf = (function(f){return function(){return f.call(this)}})(Number.prototype.valueOf)

change the valueOf and toString to whatever you need.

Warning: this is not good.

3
On

When creating a Number using new, you create a Number object, which is not a primitive - see MDN. That may be the origin of the error.

I think there are better ways to override Number methods, without polluting its prototype.

One of those is for example creating a custom Number object, using a closure to hold its value and extending that with some functions. That way you don't need protoype (or this for that matter). Something like:

function XNumber(nr = 0) {

  // the number function encloses [nr]
  const number = nrGiven => {
    nr = nrGiven;
  }
  
  const myNumberExtensions = {
    toString() { return `This is the number ${nr}`},
    set value(val) { nr = val; },
    clone(value) { return XNumber(value ?? nr); },
    increment(withValue = 1) { nr += withValue; return number; },
    valueOf() { return nr; },
  };
  
  // because Function is just another object,
  // you can extend it with custom methods
  Object.entries(Object.getOwnPropertyDescriptors(myNumberExtensions))
    .forEach( ([key, descriptor]) => { 
      Object.defineProperty(number, key, descriptor); 
    }
  );
  
  return number;
}

const myNr1 = XNumber(41);
const myNr2 = myNr1.clone().increment();

console.log(myNr1.toString());
// toString automatically when within a (template) string:
console.log(`${myNr1}`);

console.log(myNr2.valueOf());
// valueOf automatically when appended to a string
console.log(""+myNr2);