Florian Stascheck: OOP in JS und prozedural

Hallo,

Ich hab mal folgendes Beispiel entwickelt, vielleicht hilft es ja einem von euch:

  
 1:  function a(string,param) {  
 2:  this.string = string+'';  
 3:  param = param || false;  
 4:  if(!param) {  
 5:   return new a(string,true);  
 6:  }  
 7: }  
 8: var x = a('foo');  
 9: var y = new a('bar');  
10: alert(x.string+y.string);  

Es wird foobar ausgegeben. Der Sinn ist, dass eine Funktion, wenn sie prozedural aufgerufen wurde, eine Instanz von sich selbst zurückgibt. Das mit dem param ist so erprobt, dass es funktioniert. Gegebenfalls zwischen string und param weitere Parameter einfügen, in Zeile 5 muss man dann aber den Aufruf entsprechend abwandeln.

Mit der Hoffnung, dass es jemandem hilft, Flo

--
Developers are dying. Computers are getting trash. CEO's become forgetten. The only remaining things are ideas, lies and crises. So if you want to be immortal, first think, than stop it and go to microsoft and become later a manager at Lehman Brothers...
sh:) fo:| ch:? rl:( br:^ n4:| ie:{ mo:| va:} de:> zu:} fl:{ ss:) ls:< js:|
*Zu dem de:> Ich benutze wegen IE im moment noch tabellen, weil dieser display:table noch nicht versteht. Ich werde aber, wenn IE 6 & IE 7 < 10% mein neues CSS-Layout einspielen...
  1. Mit der Hoffnung, dass es jemandem hilft, Flo

    Es scheint du suchst die Eigenschaft constructor?

    function b(string) {  
     if(this.constructor != b){  
      return new b(string);  
     }  
     this.string = new String(string);  
    }  
    var x = b('foo');  
    var y = new b('bar');  
    alert(x.string + y.string);  
    
    

    Struppi.

    1. function b(string) {
      if(this.constructor != b){
        return new b(string);
      }

      if (this.constructor != arguments.callee) {
         throw new Error("Es macht wirklich keinen Sinn, einen Konstruktor ohne new aufzurufen und der es auch noch zulässt. Man sollte solche Mehrdeutigkeiten durch klare Fehlermeldungen vermeiden, anstatt zu sie durch Fehlertoleranz zu unterstützen.");
      }

      Mathias

      1. Hallo,

        if (this.constructor != arguments.callee) {
           throw new Error("Es macht wirklich keinen Sinn, einen Konstruktor ohne new aufzurufen und der es auch noch zulässt. Man sollte solche Mehrdeutigkeiten durch klare Fehlermeldungen vermeiden, anstatt zu sie durch Fehlertoleranz zu unterstützen.");

        Für ein möglichst "liberales" Framework z.B. sollte diese Option mit einem console.debug("Bitte in Zukunft soundso aufrufen") aber trotzdem gegeben sein.

        mfg, Flo

        --
        Developers are dying. Computers are getting trash. CEO's become forgetten. The only remaining things are ideas, lies and crises. So if you want to be immortal, first think, than stop it and go to microsoft and become later a manager at Lehman Brothers...
        sh:) fo:| ch:? rl:( br:^ n4:| ie:{ mo:| va:} de:> zu:} fl:{ ss:) ls:< js:|
        *Zu dem de:> Ich benutze wegen IE im moment noch tabellen, weil dieser display:table noch nicht versteht. Ich werde aber, wenn IE 6 & IE 7 < 10% mein neues CSS-Layout einspielen...
        1. Für ein möglichst "liberales" Framework z.B. sollte diese Option mit einem console.debug("Bitte in Zukunft soundso aufrufen") aber trotzdem gegeben sein.

          Solche Sachen macht man, wenn man einmal eine schlechte API definiert hat, dann merkt, dass sie schlecht war, und sie neu konzipiert, aber in der neuen Version noch abwärtskompatibel bleiben will. Dann erklärt man die API für »deprecated«, lässt sie auslaufen und entfernt sie irgendwann ganz.

          Wenn man jedoch eine API von Grund auf neu definiert, definiert man keine solchen schlechten Kompromisse. Bzw. folgendes sollte man natürlich: Prüfen, ob die Methoden korrekt aufgerufen werden, und wenn nicht, dann hat es Fehler zu hageln! Mit allem anderen (z.B. Meldungen in der Konsole - die gar nicht jeder Browser hat, in die JavaScript-Autoren nicht reinschauen, sofern kein Fehler passiert) tut man dem Programmierer überhaupt keinen Gefallen, sondern sorgt nur dafür, dass der Fehler in sein Programm einbaut, die ihm früher oder später zum Verhängnis werden.

          Ein Framework hat nicht liberal zu sein, sondern eine wohl definierte und eindeutige API zu implementieren. Die kann natürlich flexibel sein (z.B. Function Overloading), solange sie konsistent bleibt. Der Unterschied zwischen Funktionsaufruf und Konstruktoraufruf ist jedoch konzeptionell gesehen ein Unterschied ums Ganze - auch wenn es in JavaScript intern nur ein kleiner Unterschied ist (einmal wird die Funktion im Kontext eines frischen Objects, einmal im globalen Kontext ausgeführt). Wer den Unterschied nicht kennt oder ignoriert und mithilfe einer solchen »liberalen« API gänzlich einebnet, sollte m.M.n. keine objektorientierten Bibliotheken verwenden. Wenn OOP-Pattern letztlich optional sind, kann man sich die ganze Abstraktion auch sparen. Wieso sollte ich mir eine sinnhafte Objektmodellierung ausdenken und gleichzeitig eine Möglichkeit anbieten, das Konzept zu untergraben?

          Mathias

    2. Hallo,

      Es scheint du suchst die Eigenschaft constructor?

      Ich denke, schon :D

      mfg, Flo

      --
      Developers are dying. Computers are getting trash. CEO's become forgetten. The only remaining things are ideas, lies and crises. So if you want to be immortal, first think, than stop it and go to microsoft and become later a manager at Lehman Brothers...
      sh:) fo:| ch:? rl:( br:^ n4:| ie:{ mo:| va:} de:> zu:} fl:{ ss:) ls:< js:|
      *Zu dem de:> Ich benutze wegen IE im moment noch tabellen, weil dieser display:table noch nicht versteht. Ich werde aber, wenn IE 6 & IE 7 < 10% mein neues CSS-Layout einspielen...
  2. gruss Flo,

    obwohl Du ja schon sanft vom himmel geholt wurdest, kriegst Du von mir
    auch noch ein paar warme worte hinterhergereicht.

    ... Der Sinn ist, dass eine Funktion, wenn sie prozedural aufgerufen
    wurde, eine Instanz von sich selbst zurückgibt. ...

    wie schon von molily und Struppi bemerkt, willst Du das nicht wirklich;
    und es gibt sehr gute gruende, soetwas nicht zu implementieren.

    schon die sprachkernobjekte [[Boolean]], [[Number]] und [[String]]
    haben ihre berechtigung sowohl als konstruktoren von instanzen des
    jeweiligen objekttyps ([Boolean], [Number] und [String]) als auch
    als *typecast*-funktionen, die jegliche ihnen zugefuehrte objekte
    bzw. primitive werte in die wiederum primitiven werte [boolean],
    [number] bzw. [string] zwingen.

    • beispiel - einfach mal ausprobieren:

    ~~~javascript var num1 = new Number(20); alert(typeof num1); // "object"
      var num2 = Number("20"); alert(typeof num2);   // "number"
      var num3 = Number(num1); alert(typeof num3);   // "number"

    alert(num1 == num2);  // true  - impliziter typecast von "==".
      alert(num1 === num2); // false - "===" unterdrueckt typecasting.
      alert(num2 == num3);  // true  - war zu erwarten - werte sind gleich.
      alert(num2 === num3); // true  - werte sind eben auch vom gleichen typ.

      
      [num1] unterscheidet sich von [num2] und [num3] betraechtlich.  
      [num1] ist zwar zu [num2] und [num3] wert- aber nicht typgleich.  
      [num2] und [num3] hingegen sind sowohl wert- als auch typgleich.  
      
      
    zurueck zum thema - da es diesen dualismus wie man sieht schon (und  
    auch berechtigterweise) gibt, ist davon abzuraten, konstruktoren  
    selbstgestrickter objekttypen anders als die oben beispielhaft  
    angefuehrten zu implementieren.  
    dies liefe nicht nur entgegen der erwartungshaltung eines mit der  
    sprachspezifikation vertrauten anwenders, sondern verbaute auch  
    den gewuenschten effekt, dass funktional angewandte konstruktoren  
    der typwandlung dienten.  
    und es gibt durchaus objekte, die bei berechnungen oder vergleichen  
    implizit auf eigene bzw. von `[[Object]]`{:.language-javascript} bzw. anderweitig vererbte  
    `[valueOf]`{:.language-javascript} bzw. `[toString]`{:.language-javascript} methoden zugreifen.  
    eine in jeglicher hinsicht aesthetisch implementierte konstruktor-  
    funktion traegt diesem umstand dort, wo es notwendig erscheint  
    rechnung, indem sie einen geeigneten primitiven wert zurueckgibt.  
      
    links ...  
    - theoretisch unterstuetzend  : [»Funktion nicht als Konstruktor«](http://groups.google.com/group/de.comp.lang.javascript/browse_frm/thread/445e720541c03165/35818a7259c326e4?lnk=st&q=#35818a7259c326e4)  
    - durch die praxis untermauert: [»*Echte*Number-Instanzen können doch mit eigenen Methoden rechnen«](http://forum.de.selfhtml.org/archiv/2008/5/t171732/#m1125422)  
      
      
    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](http://javascript.crockford.com/)  
      
    [ie:( fl:) br:> va:( ls:& fo:) rl:) n3;} n4:} ss:} de:µ js:} mo:? zu:\]](http://www.peter.in-berlin.de/projekte/selfcode/?code=ie%3A%28+fl%3A%29+br%3A%3E+va%3A%28+ls%3A%26+fo%3A%29+rl%3A%29+n3%3B%7D+n4%3A%7D+ss%3A%7D+de%3A%B5+js%3A%7D+mo%3A%3F+zu%3A%5D)