prototipada - javascript prototype
Extender el objeto matemático a través del prototipo no funciona (4)
Intento extender JavaScript Math
. Pero una cosa me sorprendió.
Cuando intenté extenderlo por prototype
Math.prototype.randomBetween = function (a, b) {
return Math.floor(Math.random() * (b - a + 1) + a);
};
En la consola tengo el error ''No se puede establecer la propiedad'' randomBetween ''de undefined'' ...
Pero si asigno esta función a Math.__proto__
Math.__proto__.randomBetween = function (a, b) {
return Math.floor(Math.random() * (b - a + 1) + a);
};
Entonces todo funciona bien.
¿Alguien puede explicarme por qué funciona de esta manera? Aprecio cualquier ayuda.
Eso es porque hay Math
es un objeto, no una function
.
En javascript, una function
es el equivalente aproximado de una clase en lenguajes orientados a objetos. prototype
es una propiedad especial que le permite agregar métodos de instancia a esta clase 1 . Cuando quieres extender esa clase, usas prototype
y "simplemente funciona".
Ahora pensemos qué es la Math
. Nunca creas un objeto matemático, solo usas sus métodos. De hecho, no tiene sentido crear dos objetos Math
diferentes, ¡porque la Math
siempre funciona de la misma manera! En otras palabras, el objeto Math
en javascript es solo una forma conveniente de agrupar un montón de funciones relacionadas con las matemáticas preescritas. Es como un diccionario de matemáticas comunes.
¿Quieres agregar algo a ese grupo? ¡Simplemente añada una propiedad a la colección! Aquí hay dos maneras fáciles de hacerlo.
Math.randomBetween = function() { ... }
Math["randomBetween"] = function() {... }
Usar la segunda forma hace que sea un poco más obvio que es una colección de tipos de diccionarios, pero ambas hacen lo mismo.
Para citar esta respuesta :
Algunas implementaciones de JavaScript permiten el acceso directo a la propiedad [[Prototype]], por ejemplo, a través de una propiedad no estándar llamada
__proto__
. En general, solo es posible configurar el prototipo de un objeto durante la creación del objeto: si crea un nuevo objeto a través de la nueva función Func (), la propiedad [[Prototype]] del objeto se establecerá en el objeto referenciado por Func.prototype.
La razón por la que no puede asignar a su prototipo utilizando .prototype
es porque el objeto Math
ya se ha creado.
Afortunadamente para nosotros, podemos asignar nuevas propiedades al objeto Math
simplemente usando:
Math.myFunc = function() { return true };
En tu caso, esto sería:
Math.randomBetween = function(...) { ... };
Math
no es un constructor, por lo que no tiene propiedad de prototype
:
new Math(); // TypeError: Math is not a constructor
En su lugar, solo agrega tu método a Math
sí mismo como una propiedad propia :
Math.randomBetween = function (a, b) {
return Math.floor(Math.random() * (b - a + 1) + a);
};
Su enfoque con __proto__
funciona porque, dado que Math
es una instancia de Object
, Math.__proto__
es Object.prototype
.
Pero luego note que está agregando el método randomBetween
a todos los objetos, no solo a Math
. Esto puede ser problemático, por ejemplo, al iterar objetos con un bucle for...in
.
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
objetoMath
es el prototipo del nuevo objetoMyMath
-
MyMath
tiene acceso a todas las funcionalidades deMath
- Puedes agregar tu funcionalidad personalizada a
MyMath
sin manipularMath
- dentro de sus métodos personalizados, use la palabra clave
this
para referirse a la funcionalidadMath
No hay Monkey Patching con este enfoque. Esta es la mejor manera de extender JavScript Math
. No hay necesidad de repetir las explicaciones de las otras respuestas.