Error de rango al anular el método Object.prototype con el constructor de funciones
Estoy tratando de anular Object.prototype.toString
en una oferta para agregar funcionalidad para descripciones de clases adicionales.
Aquí está el código inicial:
(function(toString){
Object.prototype.toString = function(){
if(this instanceof TestClass)
{
return '[object TestClass]';
}
return toString.apply(this, arguments);
}
})(Object.prototype.toString);
function TestClass(){}
var instance_obj = new TestClass();
Object.prototype.toString.call(instance_obj);
Cuando ejecuto esto en la consola, obtengo el siguiente resultado:
[object TestClass]
Lo bueno es que no modifica drásticamente la forma en que Object.prototype.toString
funciona, por lo que con otro tipo [es decir, no TestClass], las cosas funcionan como se esperaba, por ejemplo, Object.prototype.toString.call(12)
saldrá [object Number]
.
Esta implementación funciona sin problemas hasta ahora. Sin embargo, tengo otra implementación con el siguiente código:
(function(toString){
var fn_code_str = `return function(){
if(this instanceof TestClass)
{
return '[object TestClass]';
}
return toString.apply(this, arguments);
}`;
var pre_fn = new Function(fn_code_str);
Object.prototype.toString = pre_fn();
})(Object.prototype.toString);
function TestClass(){}
var instance_obj = new TestClass();
Object.prototype.toString.call(instance_obj);
Con esto, obtengo la salida adecuada para TestClass, pero cuando uso algo más, como 12
, obtengo un RangeError:
VM527:5 Uncaught RangeError: Maximum call stack size exceeded
at Function.[Symbol.hasInstance] (<anonymous>)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:5:21)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:10:29)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:10:29)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:10:29)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:10:29)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:10:29)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:10:29)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:10:29)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:10:29)
Esto parece ser un problema con la recursividad de toString.apply
. Sin embargo, no puedo entender por qué esta segunda implementación se repite, si la primera no lo hace.
Nota : La razón de esta segunda implementación es agregar el código de verificación de tipo [es decir if(this instanceof MyClassType){return '[object MyClassType]'}
] para diferentes clases de forma dinámica desde una lista de nombres de clases en una matriz. En otras palabras, en lugar de modificar el código para cada nueva Clase que se me ocurre, agrego el nombre de la clase a la matriz y la declaración condicional se genera automáticamente.
Respuestas
El problema es que el toString
parámetro de su IIFE no está dentro del alcance de su new Function
código. En su lugar, usa el global toString
= window.toString
= Object.prototype.toString
.
Para solucionar este problema, debe declarar la toString
variable dentro del new Function
código para que el cierre devuelto funcione. O como una simple constante:
(function() {
var pre_fn = new Function(`
const toString = Object.prototype.toString;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return function(){
if(this instanceof TestClass) {
return '[object TestClass]';
}
return toString.apply(this, arguments);
}`);
Object.prototype.toString = pre_fn();
})();
o como parámetro:
(function() {
var pre_fn = new Function('toString', `
// ^^^^^^^^^^^
return function(){
if(this instanceof TestClass) {
return '[object TestClass]';
}
return toString.apply(this, arguments);
}`);
Object.prototype.toString = pre_fn(Object.prototype.toString);
// ^^^^^^^^^^^^^^^^^^^^^^^^^
})();