Расширение объекта 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
. Нет необходимости повторять объяснения из других ответов.