Расширение объекта Math через прототип не работает

Я пытаюсь расширить JavaScript Math. Но меня удивило одно.

Когда я попытался расширить его на prototype

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

В консоли у меня есть ошибка "Невозможно установить свойство" randomBetween "из undefined"...

Но если я присвою этой функции Math.__proto__

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

Тогда все работает нормально.

Может кто-нибудь объяснить мне, почему он работает таким образом? Я ценю любую помощь.

Ответы

Ответ 1

Math не является конструктором, поэтому он не имеет свойства prototype:

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

Вместо этого просто добавьте свой метод в Math самостоятельно как собственное свойство:

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

Ваш подход с __proto__ работает, потому что, поскольку Math является экземпляром Object, Math.__proto__ является Object.prototype.

Но обратите внимание, что вы добавляете метод randomBetween ко всем объектам, а не только к Math. Это может быть проблематично, например, при повторении объектов с циклом for...in.

Ответ 2

Процитировать этот ответ:

Некоторые реализации JavaScript допускают прямой доступ к свойству [[Prototype]], например, через нестандартное свойство с именем __proto__. В общем случае, только возможно установить прототип объекта при создании объекта: если вы создаете новый объект через новый Func(), свойство объекта [[Prototype]] будет установлено в объект, на который ссылается Func.prototype.

Причина, по которой вы не можете назначить свой прототип с помощью .prototype, связана с тем, что объект Math уже создан.

К счастью для нас, мы можем назначить новые свойства объекту Math, просто используя:

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

В вашем случае это будет:

Math.randomBetween = function(...) { ... };

Ответ 3

Это потому, что Math - это объект, а не function.

В javascript a function является грубым эквивалентом класса в объектно-ориентированных языках. prototype - это специальное свойство, которое позволяет добавлять методы экземпляра в этот класс 1. Если вы хотите расширить этот класс, вы используете prototype, и он "просто работает".

Теперь подумайте о том, что такое Math. Вы никогда не создаете математический объект, вы просто используете его методы. На самом деле, нет смысла создавать два разных объекта Math, потому что Math всегда работает одинаково! Другими словами, объект Math в javascript - это просто удобный способ сгруппировать кучу предварительно написанных математических функций. Это как словарь общей математики.

Хотите добавить что-то в эту группу? Просто добавьте свойство в коллекцию! Вот два простых способа сделать это.

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

Использование второго способа делает его более очевидным, поскольку это набор типа словаря, но оба они делают то же самое.

Ответ 4

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
  • объект Math является прототипом нового объекта MyMath
  • MyMath имеет доступ ко всем функциям Math
  • вы можете добавить свои пользовательские функции в MyMath без использования Math
  • в своих пользовательских методах используйте ключевое слово this для ссылки на функциональность Math

Нет подделки с Monkey с этим подходом. Это лучший способ расширить JavScript Math. Нет необходимости повторять объяснения из других ответов.