Bereichsfehler beim Überschreiben der Object.prototype-Methode mit dem Funktionskonstruktor

Nov 30 2020

Ich versuche, Object.prototype.toStringein Gebot zu überschreiben , um Funktionen für zusätzliche Klassenbeschreibungen hinzuzufügen.

Hier ist der Anfangscode:

(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);

Wenn ich dies in der Konsole ausführe, erhalte ich die folgende Ausgabe:

[object TestClass]

Das Gute ist, dass es die Funktionsweise nicht drastisch verändert. Object.prototype.toStringBei einem anderen Typ [dh nicht TestClass] funktionieren die Dinge also wie erwartet, z . B. Object.prototype.toString.call(12)werden ausgegeben [object Number].

Diese Implementierung funktioniert bisher ohne Probleme. Ich habe jedoch eine andere Implementierung mit dem folgenden Code:

(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);

Damit erhalte ich die richtige Ausgabe für TestClass, aber wenn ich etwas anderes verwende, wie z. B. 12einen 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)

Dies scheint ein Problem mit der Rekursion von zu sein toString.apply. Ich kann jedoch nicht herausfinden, warum diese zweite Implementierung rekursiv ist, wenn die erste dies nicht tut.

Hinweis : Der Grund für diese zweite Implementierung besteht darin, den Typprüfungscode [dh if(this instanceof MyClassType){return '[object MyClassType]'}] für verschiedene Klassen dynamisch aus einer Liste von Klassennamen in einem Array hinzuzufügen . Mit anderen Worten, anstatt den Code für jede neue Klasse zu ändern, füge ich stattdessen den Klassennamen an das Array an, und die bedingte Anweisung wird automatisch generiert.

Antworten

3 Bergi Nov 30 2020 at 15:42

Das Problem ist, dass der toStringParameter Ihres IIFE in Ihrem new FunctionCode nicht im Gültigkeitsbereich liegt . Stattdessen wird das globale toString= window.toString= verwendet Object.prototype.toString.

Um dies zu beheben, müssen Sie die toStringVariable im new FunctionCode des Codes deklarieren , damit der zurückgegebene Abschluss funktioniert. Entweder als einfache Konstante:

(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();
})();

oder als Parameter:

(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);
//                                     ^^^^^^^^^^^^^^^^^^^^^^^^^
})();