PoWl89: Frage zu JavaScript OOP

Hi!

In der Objektorientierten Programmierung geht es ja gerade darum, bestimmte reale oder irreale Objekte zu haben und ihnen Eigenschaften und Methoden zu geben. In HTML lässt sich das ja wunderbar anwenden, da eben jedes HTML-element so ein Objekt darstellen könnte.

Nun kann ich aus einem Element ja eine Referenz erzeugen die ja dann quasi ein Objekt darstellt.

<img src="bla.jpg" id="image">

<script>

var imageObj = document.getElementById('image');
imageObj.src = "blub.jpg";

</script>

Wenn ich nun allerdings noch Methoden und Eigenschaften dazu haben will bin ich immer so vorgegangen bisher:

<script>

function imageClass()
{
  var imageRef;

var bild;
  var nochwas;
...

this.init = function(Obj)
  {
    imageRef = document.getElementById(Obj);
  }

this.changeBorder = function(size)
  {
    imageRef.border = size;
  }
}

var imageObj = new imageClass();
imageObj.init("image");

imageObj.imageRef.src = "blub.jpg";
imageObj.changeBorder(3);

</script>

Die "Klasse" imageClass ist dazu da um Methoden und Eigenschaften für verschiedene Bilder bereit zu stellen. Nun erzeuge ich in imageObj ein neues Objekt von imageClass. Das ganze hat aber den Schönheitsfehler, dass Objekt nun nur als eine Ansammlung von Eigenschaften und Methoden alleine rumsteht, und ihr ihr HTML-element, zu dem sie gehört, nur in eine private Variable imageRef zugewiesen wird.

Wie man sieht muss jeder Zugriff aufs HTML-Objekt über diese erfolgen.
imageObj.imageRef.src = ...;

Es wäre viel schöner, wenn man eine Referenz auf ein HTML-Element erzeugen und dieser dann direkt Methoden und Eigenschaften übertragen könnte.

dann könnte ich sowohl den Pfad der Bilddatei als auch den Rahmen wie folgt ändern:
imageObj.src = "blub.jpg";  // Referenz auf HTML-element
imageObj.changeBorder(3);   // Und zugleich JS-Objekt mit Methoden und Eigenschaften

Das hätte z.B. den Vorteil dass ich das JS-Objekt direkt über das HTML-element ansprechen kann. Oder auch direkt aus dem HTML-element heraus mit this:

<img src="bla.jpg" onclick="this.changeBorder(3)">

ist sowas möglich?

lg PoWl

  1. ist sowas möglich?

    Jein. Im Firefox und Opera ja, im IE nein.

    z.b.:

    HTMLElement.prototype.info = function() {  
    alert(this.innerHTML);  
    }  
    
    

    <div onclick="this.info()">Hallo</div>

    Struppi.

  2. Hallo,

    Außer der prototypischen Erweiterung kannst du natürlich einem img-Elementobjekt eigene Methoden anhängen. Das gilt dann natürlich nicht standardmäßig für alle img-Elemente, sondern nur für die, die man so »behandelt« und ergänzt hat.

    function ImageConstructor (id) {  
       var element = document.getElementById(id);  
       element.methode = function () { ... };  
       return element;  
    }  
    var imageObj = new ImageConstructor();  
    imageObj.methode();
    

    <img onclick="this.methode()"> funktioniert dann, wenn das Element nach dem Erweitern geklickt wird.

    Mathias

    1. Hey, das ist richtig gut :-)

      Danke, genau sowas suche ich! Funktioniert das in allen Browsern?

      1. Danke, genau sowas suche ich! Funktioniert das in allen Browsern?

        Ja.

        Struppi.

      2. Funktioniert das in allen Browsern?

        Ja, schließlich werden dabei bloß (Element-)Objekten Eigenschaften hinzugefügt, das funktioniert mit allen Objekten.

        Mathias

    2. Noch eine Zusatzfrage:

      Was ist ImageConstructor() eigentlich? Ist das nun eine normale Funktion oder ein Objekt?

      Sieht nach einer normalen Funktion aus, die in der variable element eine referenz auf das HTML-element speichert, diesem dann die methode "methode" anfügt und die referenz zurrückgibt.

      Wozu ist dann allerdings das Schlüsselwort new nötig?
      var imageObj = new ImageConstructor();

      Diese Möglichkeiten schließen dabei aber aus, dass ich dem HTML-element auch private eigenschaften und methoden gebe, richtig?

      lg PoWl

      1. Was ist ImageConstructor() eigentlich? Ist das nun eine normale Funktion oder ein Objekt?

        Ein normale Funktion ist immer ein Objekt, das ist aber ein Art Decorator wenn ich mich nicht irre.

        Sieht nach einer normalen Funktion aus, die in der variable element eine referenz auf das HTML-element speichert, diesem dann die methode "methode" anfügt und die referenz zurrückgibt.

        Ja.

        Wozu ist dann allerdings das Schlüsselwort new nötig?
        var imageObj = new ImageConstructor();

        Das ist in der Tat überflüssig.

        Diese Möglichkeiten schließen dabei aber aus, dass ich dem HTML-element auch private eigenschaften und methoden gebe, richtig?

        Du kannst durchaus private Variabeln deklarieren in der Funktion deklarieren, diese gelten dann nur für das jeweilige Objekt.

        z.b.:

        function ImageConstructor (id) {  
         var element = document.getElementById(id);  
         var counter = 0;  
         element.onclick = function () {  
          alert(counter);  
          counter++;  
         };  
         return element;  
        }
        

        Struppi.

      2. gruss PoWl, hallo molily, hallo Struppi;

        Was ist ImageConstructor() eigentlich? Ist das nun eine normale
        Funktion oder ein Objekt?

        aus dem bauch heraus haette ich gesagt, dass es sich um einen
        »Wrapper« (»Hüllenklasse« bzw. »Adapter«) handelt, moeglicherweise
        hat aber allein Struppi mit dem »Decorator« recht.

        Wozu ist dann allerdings das Schlüsselwort new nötig?

        das ist, wie schon angemerkt, in der tat zuviel des guten.

        anhand Deiner fragestellung laesst sich beispielhaft sehr gut die
        anwendung von interfaces in JavaScript zeigen. dabei wird eine
        funktion eben nicht als konstruktor zur instanziierung herangezogen.
        ein schon existierendes objekt kann sich aber dieser funktion
        bedienen, um dort auf oeffentlich (ueber [this]) vereinbarte
        eigenschaften bzw. noch viel mehr auf solche methoden zuzugreifen,
        um diese in seinem kontext auszufuehren.

        code:

        var EnhancedImage = (function () { // not to be used as constructor but as kind of interface  
          
          
         this.getSource = (function() {  
          
          return this.src;  
          });  
         this.setSource = (function(str) {  
          
          this.src = String(string);  
          });  
         this.getBorderWidth = (function() {  
          
          return this.border;  
          });  
         this.setBorderWidth = (function(num) {  
          
          this.border = Number(num);  
          });  
        });  
          
          
        var myImg = document.images[0];  
          
          
        EnhancedImage.call(myImg); // [[EnhancedImage]] als interface auf [myImg] anwenden.  
          
          
        alert("myImg.getSource() : " + myImg.getSource()); // http://src.selfhtml.org/logo.gif  
          
        myImg.setBorderWidth(3);  
          
        alert("myImg.getBorderWidth() : " + myImg.getBorderWidth()); // 3  
          
          
        /*  
         naechste zeile bei geoeffneten SELFHTML Forum mal auf die locationbar des browsers loslassen:  
          
         javascript:var EnhancedImage = (function () {this.getSource = (function() {return this.src;});this.setSource = (function(str) {this.src = String(string);});this.getBorderWidth = (function() {return this.border;});this.setBorderWidth = (function(num) {this.border = Number(num);});});var myImg = document.images[0];EnhancedImage.call(myImg);alert("myImg.getSource() : " + myImg.getSource());myImg.setBorderWidth(3);alert("myImg.getBorderWidth() : " + myImg.getBorderWidth());  
        */
        

        verfolgt man die idee nun weiter, um diese art der delegation noch komfortabler
        ueber eigene methoden abzubilden, kommt man sicher auf aehnliche loesungen, wie
        die beiden folgenden:

        Function.prototype.implementedBy = (function (/*obj:Object|Function[, obj:Object|Function[, ...]]*/) {  
          
          
         var fct = this;  
          
         [link:http://forum.de.selfhtml.org/archiv/2007/3/t148291/@title=Array.forEach](arguments, (function (obj/*, idx, list*/) {  
          
          if (obj && ((typeof obj == "object") || (typeof obj == "function"))) {  
          
           fct.call(obj);  
          }  
         }));  
        });  
          
          
        Object.prototype.implements = (function(/*interface:Function[, interface:Function[, ...]]*/) {  
          
          
         var obj = this;  
          
         [link:http://www.pseliger.de/jsExtendedApi/jsApi.Array.mozGenerics.dev.js@title=Array.forEach](arguments, (function (fct/*, idx, list*/) {  
          
          if (typeof fct == "function") {  
          
          //fct.implementedBy(obj);  
           fct.call(obj);  
          }  
         }));  
        });
        

        das oben angefuehrte beispiel laesst sich in diesem fall nur mit
        [function].implementedBy([object]) umsetzen, da [HTMLImageElement]e
        als host-objekte bzw. objekte des DOM nicht in allen browsern
        nativ prototypisch implementiert wurden.

        [object].implements([function]) funktioniert wie gerade angedeutet
        nur mit nativen JavaScript-objekten (also mit allen kern-objekten und
        mit allen darauf basierenden selbstgetrickten objekten).

        proof of concept:

        Function.prototype.implementedBy = (function (/*obj:Object|Function[, obj:Object|Function[, ...]]*/) {  
          
          
         var fct = this;  
          
         Array.forEach(arguments, (function (obj/*, idx, list*/) {  
          
          if (obj && ((typeof obj == "object") || (typeof obj == "function"))) {  
          
           fct.call(obj);  
          }  
         }));  
        });  
          
          
        var EnhancedImage = (function () {  
          
          
         this.getSource = (function() {  
          
          return this.src;  
          });  
         this.setSource = (function(str) {  
          
          this.src = String(string);  
          });  
         this.getBorderWidth = (function() {  
          
          return this.border;  
          });  
         this.setBorderWidth = (function(num) {  
          
          this.border = Number(num);  
          });  
        });  
          
          
        var myImg = document.images[0];  
          
          
        //EnhancedImage.call(myImg); // [[EnhancedImage]] als interface auf [myImg] anwenden.  
          
        EnhancedImage.implementedBy(myImg); // [[EnhancedImage]] als interface auf [myImg] anwenden.  
          
          
        alert("myImg.getSource() : " + myImg.getSource()); // http://src.selfhtml.org/logo.gif  
          
        myImg.setBorderWidth(3);  
          
        alert("myImg.getBorderWidth() : " + myImg.getBorderWidth()); // 3  
          
          
        /*  
         naechste zeile bei geoeffneten SELFHTML Forum mal auf die locationbar eines gecko-browsers loslassen:  
          
         javascript:Function.prototype.implementedBy = (function () {var fct = this;Array.forEach(arguments, (function (obj) {if (obj && ((typeof obj == "object") || (typeof obj == "function"))) {fct.call(obj);}}));});var EnhancedImage = (function () {this.getSource = (function() {return this.src;});this.setSource = (function(str) {this.src = String(string);});this.getBorderWidth = (function() {return this.border;});this.setBorderWidth = (function(num) {this.border = Number(num);});});var myImg = document.images[0];EnhancedImage.implementedBy(myImg);alert("myImg.getSource() : " + myImg.getSource());myImg.setBorderWidth(3);alert("myImg.getBorderWidth() : " + myImg.getBorderWidth());  
        */
        

        so long - peterS. - pseliger@gmx.net

        --
        »Because objects in JavaScript are so flexible, you will want to think differently about class hierarchies.
        Deep hierarchies are inappropriate. Shallow hierarchies are efficient and expressive.« - Douglas Crockford
        ie:( fl:) br:> va:( ls:& fo:) rl:) n3;} n4:} ss:} de:µ js:} mo:? zu:]
        1. Hallo peterS,

          Wow, du bist ein Gott in JS, Kompliment!
          Hast mir mal mit einem selbtgestrickten Number-Objekt geholfen, falls du dich erinnerst. Das setze ich seither mit Erfolg ein. Vielen Dank nochmal an dieser Stelle.

          Gruß, Don P

          1. gruss Don P

            Hast mir mal mit einem selbtgestrickten Number-Objekt geholfen,
            falls du dich erinnerst. Das setze ich seither mit Erfolg ein. Vielen
            Dank nochmal an dieser Stelle.

            ja, na klar erinnere ich mich - seitdem warte ich ja auf die gelegenheit,
            Dir die endgueltige loesung zu servieren. leider war der thread nach
            meinem damaligen letzten posting sehr schnell dem *schwanzabschneider*
            zum opfer gefallen - zumindest hast Du Dich zwischen diesem posting
            und der archivierung des thraeds nicht mehr im forum blicken lassen.

            hier der link ins archiv:

            EchteNumber-Instanzen können doch mit eigenen Methoden rechnen

            viel spass - so long - peterS. - pseliger@gmx.net

            --
            »Because objects in JavaScript are so flexible, you will want to think differently about class hierarchies.
            Deep hierarchies are inappropriate. Shallow hierarchies are efficient and expressive.« - Douglas Crockford
            ie:( fl:) br:> va:( ls:& fo:) rl:) n3;} n4:} ss:} de:µ js:} mo:? zu:]
            1. Hallo,

              Ja, das hatte ich gesehen, aber mich nicht mehr gemeldet. Deshalb wollte ich mich hier nochmal bedanken. Ein Number-Objekt mit zusätzlichen Eigenschaften u. Methoden ist wirklich sehr hilfreich für manche Zwecke.

              Gruß, Don P