Extending Math object through prototype doesn't work
Asked Answered
B

5

27

I try to extend JavaScript Math. But one thing surprised me.

When I tried to extend it by prototype

Math.prototype.randomBetween = function (a, b) {
    return Math.floor(Math.random() * (b - a + 1) + a);
};

In console I have error 'Cannot set property 'randomBetween' of undefined' ...

But if I asigne this function to Math.__proto__

Math.__proto__.randomBetween = function (a, b) {
    return Math.floor(Math.random() * (b - a + 1) + a);
};

Then everything works fine.

Can anybody explain me why it works in this way? I appreciate any help.

Bathsheba answered 20/12, 2014 at 13:56 Comment(0)
T
43

Math isn't a constructor, so it doesn't have prototype property:

new Math(); // TypeError: Math is not a constructor

Instead, just add your method to Math itself as an own property:

Math.randomBetween = function (a, b) {
    return Math.floor(Math.random() * (b - a + 1) + a);
};

Your approach with __proto__ works because, since Math is an Object instance, Math.__proto__ is Object.prototype.

But then note you are adding randomBetween method to all objects, not only to Math. This can be problematic, for example when iterating objects with a for...in loop.

Torn answered 20/12, 2014 at 13:58 Comment(3)
@GeorgeJempty Why do you not like "own property"?Torn
@GeorgeJempty an "own property" in JavaScript is a property that exists directly on a particular object (as opposed to one that is inherited via the prototype chain). Maybe it would be easier to look at if it had quotes around it or were in italics?Dayak
Thanks for the clarification, quotes would help though otherwise it looks like mangled EnglishHidebound
Z
5

That's because there's Math is an object, not a function.

In javascript, a function is the rough equivalent of a class in object oriented languages. prototype is a special property which lets you add instance methods to this class1. When you want to extend that class, you use prototype and it "just works".

Now let's think about what Math is. You never create a math object, you just use it's methods. In fact, it doesn't make sense to create two different Math objects, because Math always works the same! In other words, the Math object in javascript is just a convenient way to group a bunch of pre-written math related functions together. It's like a dictionary of common math.

Want to add something to that group? Just add a property to the collection! Here's two easy ways to do it.

Math.randomBetween = function() { ... }
Math["randomBetween"] = function() {... }

Using the second way makes it a bit more obvious that it's a dictionary type collection, but they both do the same thing.

Zaller answered 20/12, 2014 at 19:57 Comment(0)
M
3

To quote this answer:

Some JavaScript implementations allow direct access to the [[Prototype]] property, eg via a non-standard property named __proto__. In general, it's only possible to set an object's prototype during object creation: If you create a new object via new Func(), the object's [[Prototype]] property will be set to the object referenced by Func.prototype.

The reason you can't assign to its prototype using .prototype is because the Math object has already been created.

Fortunately for us, we can assign new properties to the Math object by simply using:

Math.myFunc = function() { return true };

In your case, this would be:

Math.randomBetween = function(...) { ... };
Mccutcheon answered 20/12, 2014 at 14:1 Comment(1)
To be fair as much as I hate __proto__ it has since been standardized and works on all modern browsers.Sike
G
2
var MyMath = Object.create(Math); // empty object with prototype Math

MyMath.randomBetween = function (a, b) {
    return this.floor(this.random() * (b - a + 1) + a);
};

typeof(MyMath);                 // object
Object.getPrototypeOf(MyMath);  // Math
MyMath.PI;                      // 3.14...
MyMath.randomBetween(0, 10);    // exactly that
  • the Math object is the prototype of the new MyMath object
  • MyMath has access to all functionality of Math
  • you can add your custom functionality to MyMath without manipulating Math
  • within your custom methods, use keyword this to refer to the Math functionality

There is no Monkey Patching with this approach. This is the best way to extend JavScript Math. There is no need to repeat the explanations from the other answers.

Goodish answered 28/5, 2017 at 19:4 Comment(0)
C
1

While Math is an actual Object not a constructible class you can use spread operator: {...obj} to extend the Math Object like this:

const ExtendMath = {
    ...Math,
    randomInt(max, min = 0){Math.floor(Math.random() * (max - min + 1)) + min},
}

now the randomInt() method will be added to the Math object as an extended method.

Charisecharisma answered 16/5, 2023 at 10:52 Comment(3)
The Math object won't be changed. Did you mean "will be added to the Math object"?Cockayne
yes exactly, it will not be added directly to the Math object but it will be added to the ExtendMath object.Charisecharisma
but in your answer, you say the "method will be added to the Math object"Cockayne

© 2022 - 2024 — McMap. All rights reserved.