Erreur de plage lors de la substitution de la méthode Object.prototype avec le constructeur Function
J'essaie de remplacer Object.prototype.toString
dans une offre pour ajouter des fonctionnalités pour des descriptions de classe supplémentaires.
Voici le code initial:
(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);
Lorsque je l'exécute dans la console, j'obtiens la sortie suivante:
[object TestClass]
La bonne chose est qu'il ne modifie pas radicalement la façon dont les Object.prototype.toString
travaux, donc avec un autre type [ie non TestClass], les choses fonctionnent comme prévu par exemple la Object.prototype.toString.call(12)
production de volonté [object Number]
.
Cette implémentation fonctionne sans aucun problème jusqu'à présent. Cependant, j'ai une autre implémentation avec le code suivant:
(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);
Avec cela, j'obtiens la sortie appropriée pour TestClass, mais lorsque j'utilise autre chose, comme 12
, j'obtiens 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)
Cela semble être un problème avec la récursivité de toString.apply
. Cependant, je ne peux pas comprendre pourquoi cette deuxième implémentation est récurrente, si la première ne le fait pas?
Remarque : La raison de cette seconde implémentation est d'ajouter if(this instanceof MyClassType){return '[object MyClassType]'}
dynamiquement le code de vérification de type pour différentes classes à partir d'une liste de noms de classe dans un tableau. En d'autres termes, plutôt que de modifier le code pour chaque nouvelle classe que je propose, j'ajoute le nom de la classe au tableau à la place, et l'instruction conditionnelle est générée automatiquement.
Réponses
Le problème est que le toString
paramètre de votre IIFE n'est pas dans la portée de votre new Function
code. Au lieu de cela, il utilise le global toString
= window.toString
= Object.prototype.toString
.
Pour résoudre ce problème, vous devez déclarer la toString
variable dans le new Function
code de pour que la fermeture retournée fonctionne. Soit comme une 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();
})();
ou en paramètre:
(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);
// ^^^^^^^^^^^^^^^^^^^^^^^^^
})();