molily: Klasse eines Objektes feststellen

Hallo zusammen,

Vorweg: Ich habe keine wirkliche Ahnung und demnach wirbeln wahrscheinlich die Begriffe durcheinander, bitte korrigiert mich, wenn ich unverständlich schreibe bzw. etwas offenbar ungenau bezeichnet oder dargestellt habe.

Ich möchte von einem beliebigen Objekt in Erfahrung bringen, von welcher Klasse es abstammt bzw. welche(s) Prototypobjekt(e) es hat, und zwar ohne von bekannten Eigenschaften und Methoden darauf zu schließen.

Nehmen wir beispielsweise ein Arrayobjekt:

var meinarray=['a', 'b', 'c'];

Nun würde »typeof meinarray« logischerweise »Object« liefern. Es ließen sich natürlich Array-Methoden nachweisen, was ich aber nicht möchte. Natürlich ließe sich auch feststellen, dass das Objekt eine Instanz des Array-Objektes ist:

window.alert(meinarray instanceof Array);
window.alert(Array.prototype.isPrototypeOf(meinarray));

Aber dann müsste der Typ Array bekannt sein, ich gehe aber davon aus, dass er nicht bekannt ist und somit auch keine solchen Prüfungen vorgenommen werden können. Umständlich ginge es natürlich folgendermaßen:

Array.prototype.toString=Object.prototype.toString;
/\s([^]]+)]$/.exec(meinarray.toString());
window.alert(RegExp.$1);

Aber es ist genauso unpraktisch bzw. unmöglich, die toString-Methode eines jeden Prototypenobjektes zu verändern, sodass es wieder darauf hinausläuft, dass zumindest eine Vermutung existiert, um welchen Prototyp es sich handelt.

Wenn es um selbstdefinierte Konstruktoren geht, ist es natürlich möglich, einem Objekt eine Eigenschaft mitzugeben, anhand welcher es identifizierbar ist:

function beispielobjekt () {this.type=new String('beispielobjekt');}

Angenommen, es wird ein Objekt definiert:
var meinobjekt=new beispielobjekt();

Wie kann ich aber nun unabhängig den genannten indirekten Techniken direkt abfragen, welches das jeweilige Prototypobjekt ist?

Im ECMA262-Standard habe ich nur einen internen Wert [[Class]] gefunden, welcher sich aber anscheinend nicht von außen abfragen lässt...

(Habe ich vielleicht irgendetwas komplett falsch verstanden?)

Grüße,
Mathias

  1. Hallo,

    Ich möchte von einem beliebigen Objekt in Erfahrung bringen, von welcher Klasse es abstammt bzw. welche(s) Prototypobjekt(e) es hat, und zwar ohne von bekannten Eigenschaften und Methoden darauf zu schließen.

    Du könntest die constructor-Eigenschaft der Instanz verwenden. Diese liefert die Constructor-Funktion oder das Constructor-Objekt, wenn es sich um ein direkt eingebundenes Objekt handelt (bsp. IMAGE). Diese beiden Fälle könnte eine function getPrototype(obj) berücksichtigen und die entsprechenden Prototyp-Name herauslesen.

    Beispiel:

    <script type="text/javascript">
    <!--
    //Prototyp MyPerson
    function MyPerson(name) {
      var iName = name;
      this.getName = function() {
        return iName;
      }
      this.setName = function(name) {
        iName = name;
      }
    }

    //Prototyp MyAnschrift erweitert MyPerson
    function MyAnschrift(name, ort) {
      var iOrt = ort;
      this.prototype = MyPerson;
      this.prototype(name);
      this.getAnschrift = function() {
        return this.getName() + " " + iOrt;
      }
      this.setAnschrift = function(name, ort) {
        this.setName(name);
        iOrt = ort;
      }
    }

    //Prototypen beliebiger Objekte ermitteln
    function getPrototype(obj) {
      var iPrototype = undefined;
      if (obj.constructor) {
        if (obj.constructor.toString().indexOf("function") >=0 && obj.constructor.toString().indexOf("function") <=1 && obj.constructor.toString().indexOf("(") > 9) {
          iPrototype = obj.constructor.toString().substring(9, obj.constructor.toString().indexOf("("));
        } else if (obj.constructor.toString().indexOf("[") == 0) {
          iPrototype = obj.constructor.toString();
        }
      }
      return iPrototype;
    }

    var myP = new MyPerson("Müller");
    document.writeln(myP.getName());
    myP.setName("Maier");
    document.writeln(myP.getName());
    document.writeln("<br>");

    var myA = new MyAnschrift("Müller", "Cottbus");
    document.writeln(myA.getAnschrift());
    myA.setAnschrift("Maier", "Köln");
    document.writeln(myA.getAnschrift());
    document.writeln("<br>");
    document.writeln("<br>");

    document.writeln("Prototypen ermitteln");
    document.writeln("<br>");

    document.writeln(getPrototype(myP));
    document.writeln("<br>");

    document.writeln(getPrototype(myA));
    document.writeln("erweitert");
    document.writeln(getPrototype(myA.prototype));
    document.writeln("<br>");

    myI = new Image();
    document.writeln(getPrototype(myI));
    document.writeln("<br>");

    myArr = new Array(5);
    document.writeln(getPrototype(myArr));
    document.writeln("<br>");

    myFunc = function() {return};
    document.writeln(getPrototype(myFunc));
    document.writeln("<br>");

    function MyImage() {
      this.prototype = new Image();
    }
    myMyI = new MyImage();
    document.writeln(getPrototype(myMyI));
    document.writeln("erweitert");
    document.writeln(getPrototype(myMyI.prototype));
    document.writeln("<br>");

    function MyArray() {
      this.prototype = new Array();
    }
    myMyArr = new MyArray();
    document.writeln(getPrototype(myMyArr));
    document.writeln("erweitert");
    document.writeln(getPrototype(myMyArr.prototype));
    document.writeln("<br>");

    //-->
    </script>

    Zu testen wäre, ob wirklich nur die beiden behandelten Fälle existieren und ob die DOM-fähigen Browser alle gleiche Werte für obj.constructor.toString() liefern. Getestet habe ich MSIE5.5 und Mozilla1.3.1. Hier gab es schon Unterschiede. MSIE liefert bei Funktionen als Constructor obj.constructor.toString().indexOf("function") == 0, Mozilla liefert obj.constructor.toString().indexOf("function") == 1.

    viele Grüße

    Axel

    1. Hallo Axel,

      Ich möchte von einem beliebigen Objekt in Erfahrung bringen, von welcher Klasse es abstammt bzw. welche(s) Prototypobjekt(e) es hat, und zwar ohne von bekannten Eigenschaften und Methoden darauf zu schließen.

      Du könntest die constructor-Eigenschaft der Instanz verwenden. Diese liefert die Constructor-Funktion oder das Constructor-Objekt, wenn es sich um ein direkt eingebundenes Objekt handelt (bsp. IMAGE). Diese beiden Fälle könnte eine function getPrototype(obj) berücksichtigen und die entsprechenden Prototyp-Name herauslesen.

      Argh, darauf hätte ich durch ein wenig mehr Lektüre selbst kommen können. Wahrscheinlich hatte ich es sogar gelesen, aber es ging wohl an mir vorüber... Vielen Dank, jetzt fällt es mir wie Schuppen von den Augen.

      Danke auch für die umfangreichen Beispiele!

      Zu testen wäre, ob wirklich nur die beiden behandelten Fälle existieren und ob die DOM-fähigen Browser alle gleiche Werte für obj.constructor.toString() liefern.

      Im MSIE 6 hat ein mit new Image() erstelltes Objekt keine constructor-Eigenschaft (undefined), ist mir aufgefallen.
      Opera 6 spielt bei eigenen Objekten auch nicht mit. Blamabel, denn sogar Netscape 4 bekommt es hin.

      Getestet habe ich MSIE5.5 und Mozilla1.3.1. Hier gab es schon Unterschiede. MSIE liefert bei Funktionen als Constructor obj.constructor.toString().indexOf("function") == 0, Mozilla liefert obj.constructor.toString().indexOf("function") == 1.

      Ja, verschiedene Browser werfen Zeilenumbrüche vorher und nachher ein, ich werde einfach alle Whitespaces vorne und hinten im String abschneiden, bevor der Objektname extrahiert wird. Vielleicht nutze ich auch Reguläre Ausdrücke für die Erkennung.
      (Ah, ich sehe gerade Peters Vorschlag... so in der Art stelle ich mir das vor.)

      Ich muss gestehen, dass es sowieso eine mehr oder weniger theoretische Frage war (es ging mir eigentlich um die Linearisierung von Objekten), deshalb ist Kompatibilität für die Lösung kein Muss.

      Grüße,
      Mathias

      --
      »In anderen Newsgroups werden Pseudonyme akzeptiert, es handelt sich dabei meist um Gruppen, in denen sensible Themen (z.B. psychische oder peinliche Erkrankungen o.ä.) behandelt werden.«
  2. gruss Matthias,

    wie Axel schon sagte, hast Du nur die moeglichkeit,
       mit dem konstruktor Deines js-objektes zu arbeiten;

    eine alle js-objekte umfassende loesung saehe dann
       so aus:

    Object.prototype.getConstructorName = function() {
          var thisConstructor = this.constructor;
          var thisConstructorString = thisConstructor.toString();
          var regExpDefault = /^\s*function\s+([a-zA-Z0-9_(]{2,}[^)]))/;
          var regExpMsie5x = /^\s*[object\s+([a-zA-Z0-9_]{1,})/;
          return ((thisConstructor) ? ((regExpDefault.test(thisConstructorString)) ? (regExpDefault.exec(thisConstructorString)[1] + ")") : ((regExpMsie5x.test(thisConstructorString)) ? (regExpMsie5x.exec(thisConstructorString)[1] + "()") : (null))) : (null));
       };
    // liefert den namen des objekt-konstruktors zurueck (bei einem oo-klassenkonzept entspraeche dies dem klassennamen);

    oder auch so:

    Object.prototype.getObjectType = function() {
          var thisConstructor = this.constructor;
          var thisConstructorString = thisConstructor.toString();
          var regExpDefault = /^\s*function\s+([a-zA-Z0-9_]{1,})/;
          var regExpMsie5x = /^\s*[object\s+([a-zA-Z0-9_]{1,})/;
          return ((thisConstructor) ? ((regExpDefault.test(thisConstructorString)) ? (regExpDefault.exec(thisConstructorString)[1].toLowerCase()) : ((regExpMsie5x.test(thisConstructorString)) ? (regExpMsie5x.exec(thisConstructorString)[1].toLowerCase()) : ("object"))) : ("object"));
       };
    // liefert einen genaueren objekt-typen zurueck als die eingebaute globale funktion "typeof";

    damit sollten sich nun die probleme aus Deinen fragestellungen erschlagen lassen:

    ..
    Wenn es um selbstdefinierte Konstruktoren geht, ist es natürlich möglich, einem Objekt
    eine Eigenschaft mitzugeben, anhand welcher es identifizierbar ist:

    function Beispielobjekt() {this.type=new String('beispielobjekt');}

    Angenommen, es wird ein Objekt definiert:
    var meinobjekt=new Beispielobjekt();

    alert(meinobjekt.getConstructorName()); // sollte "Beispielobjekt()" ausgeben;
       alert(meinobjekt.getObjectType()); // sollte "beispielobjekt" ausgeben;

    Wie kann ich aber nun unabhängig den genannten indirekten Techniken direkt abfragen
    welches das jeweilige Prototypobjekt ist?

    Im ECMA262-Standard habe ich nur einen internen Wert [[Class]] gefunden, welcher sich
    aber anscheinend nicht von außen abfragen lässt...

    (Habe ich vielleicht irgendetwas komplett falsch verstanden?)

    jein:
       - wenn Du nach der klasse fragst, meinst Du "Object.constructor";
       - "Object.prototype" ist ein anderes paar schuhe;

    viel spass und by(t)e by(t)e - peterS. - pseliger@gmx.net

    --
    sh:| fo:) ch:? rl:| br:& n3:} n4:# ie:| mo:{ va:| de:[ zu:] fl:) ss:) ls:& js:)

    1. Hallo Peter,

      eine alle js-objekte umfassende loesung saehe dann
         so aus:

      Object.prototype.getConstructorName = function() {
            var thisConstructor = this.constructor;
            var thisConstructorString = thisConstructor.toString();
            var regExpDefault = /^\s*function\s+([a-zA-Z0-9_(]{2,}[^)]))/;
            var regExpMsie5x = /^\s*[object\s+([a-zA-Z0-9_]{1,})/;
            return ((thisConstructor) ? ((regExpDefault.test(thisConstructorString)) ? (regExpDefault.exec(thisConstructorString)[1] + ")") : ((regExpMsie5x.test(thisConstructorString)) ? (regExpMsie5x.exec(thisConstructorString)[1] + "()") : (null))) : (null));
         };
      // liefert den namen des objekt-konstruktors zurueck (bei einem oo-klassenkonzept entspraeche dies dem klassennamen);

      oder auch so:

      Object.prototype.getObjectType = function() {
            var thisConstructor = this.constructor;
            var thisConstructorString = thisConstructor.toString();
            var regExpDefault = /^\s*function\s+([a-zA-Z0-9_]{1,})/;
            var regExpMsie5x = /^\s*[object\s+([a-zA-Z0-9_]{1,})/;
            return ((thisConstructor) ? ((regExpDefault.test(thisConstructorString)) ? (regExpDefault.exec(thisConstructorString)[1].toLowerCase()) : ((regExpMsie5x.test(thisConstructorString)) ? (regExpMsie5x.exec(thisConstructorString)[1].toLowerCase()) : ("object"))) : ("object"));
         };
      // liefert einen genaueren objekt-typen zurueck als die eingebaute globale funktion "typeof";

      *g*
      Sind die REGEXP von Dir? Kann das süchtig machen? ;-))

      Probleme:
      Wofür sind die regExpMsie5x? Mein MSIE5.5 läuft auch mit den regExpDefault genau so wie Mozilla1.3.1.

      Einen Fehler hat MSIE5.5 allerdings. IMAGE ist für ihn kein Object im Sinne von JavaScript. Es ist eben ein natives Objekt. Deswegen bringt folgendes den Fehler:
      Objekt unterstützt diese Eigenschaft oder Methode nicht.

      Klar, getConstructorName() und getObjectType() wurden dem nativen IMAGE-Objekt gar nicht zugewiesen.

      myI = new Image();
      document.writeln(myI.getConstructorName());
      document.writeln(myI.getObjectType());

      Mozilla macht das. Allerdings bringt
      myI.getConstructorName() == null und
      myI.getObjectType() == "objekt".

      viele Grüße

      Axel

    2. Hallo Peter,

      danke auch dir für die schöne Lösung, das hilft mir sehr. Wobei ich sagen muss, dass ich lieber längeren, ausführlichen und einfach lesbareren Code schreibe, beispielsweise lieber if-Anweisungen anstatt den mehrere ?:-Operatoren in einer Zeile benutze. ;) (Dein Code ist durchaus verständlich, so meine ich es nicht.)

      Du hast mich auch darauf hingewiesen, dass die exec-Methode einen Array zurückliefert, ich lese wohl an allem vorbei, es steht sogar in Selfhtml. Ich wunderte mich schon, wieso die Umwege nötig sind...

      Das mit Image ist mir auch aufgefallen, ich werde wohl Angaben wie [HTMLImageElement] speziell berücksichtigen müssen, aber das hat Axel ja bereits angemerkt.

      Grüße,
      Mathias