Funktionen mit . aneinander hängen - a().b() ?

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Funktionen mit . aneinander hängen - a().b() ?

    Hallo Leute,

    ich würde gerne Funktionen aneinander hängen, vielleicht ist aneinander hängen auch nicht der richtige Ausdruck ?( man kann in reinem Javascript a().b() schreiben
    aber was sind die Bedingungen dafür? Hier ein Beispiel:

    JavaScript-Quellcode

    1. //var x = a().b() // Funktioniert so nicht
    2. //console.log(x) // Funktioniert so nicht
    3. console.log(a()) // Funktioniert so auch nicht
    4. var a = function(){
    5. return 1
    6. }
    7. var b = function(){
    8. return 1
    9. }
    10. var c = function(){
    11. return 1
    12. }
    Alles anzeigen
    Ich kann noch nichtmal a() so aufrufen, warum? und warum kann ich nicht a().b() aufrufen? Ich dachte ich bekomme zumindest 11 als Ausgabe also - zweimal 1 nicht 11 8|

    Danke und Grüße

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von vision27 ()

  • So meinte ich das:

    JavaScript-Quellcode

    1. //console.log(counter.add(5).add(3).value) // Geht nicht wenn das Funktionsobjekt danach definiert wird
    2. var counter = {
    3. value: 0,
    4. add: function(x){ // geht nicht mit dem => Operator
    5. this.value += x;
    6. return this;
    7. }
    8. }
    9. counter.add(1).add(1).add(1)
    10. console.log(counter.value) // 3
    11. /*
    12. Als richtiges Objekt mit new Konstruktor, dadurch kann die Definition später stattfinden
    13. */
    14. var c = new Counter()
    15. c.add(1).add(1).add(1)
    16. console.log(c.value) // 3
    17. function Counter(){
    18. this.value = 0
    19. this.add = (x) => { // Geht mit function und mit dem => Operator
    20. this.value += x
    21. return this
    22. }
    23. }
    Alles anzeigen
  • Wie du selbst schon richtig erkannt hast, musst du für chaining immer ein Objekt zurückgeben.
    Wenn wir uns mal die Operatoren aus deinem Beispiel a().b() anschauen, dann sehen wir 2x () (call) womit, wenn möglich, eine Funktion aufgerufen werden soll. Zudem gibt es aber auch ein Mal . (object accessor) mit dem man auf eine Eigenschaft/property eines Objektes zugreift.
    Aufgelöst wird von links nach rechts. Nach dem Call von a steht dann da 1.b(). Da es aber kein 1.b gibt, kommt hier der Fehler.

    Chaining kommt entsprechend aus der Objektorientierten Programmierung. Es gibt aber auch einen Ansatz aus der Funktionellen Programmierung.
    Im Moment ist es nur möglich die Funktionsaufrufe ineinander zu verschachteln (c(b(a()))). Hierbei wird das Ergebnis der inneren Funktion an die äußere übergeben. Die Nachteile sind aber eindeutig:
    • rechts nach links Leserichtung
    • unübersichtlich
    • "Klammerzirkus"
    Daher ist zur Zeit eine neue Schreibweise im Gespräch, die in einer zukünftigen Version von JS veröffentlicht werden könnte. Mit dem Pipeline Operator könnte das auch so aussehen: a |> b |> c.


    Zu deinen Fragen:

    Felix K. schrieb:

    Ich kann noch nichtmal a() so aufrufen, warum?
    Das hängt mit dem hoisting (ein Interpretationsschritt) zusammen. Als du a aufgerufen hast, war es noch undefined und noch kleine Funktion. Beim hoisting werden alle Definitionen mit var oder function (und ausschließlich diese) nach ganz oben verschoben. Hättest du function a() {...} anstelle von var a = function() {...} verwendet, hätte es funktioniert.

    Deine Datei dürfte nach dem hoisting so ausgesehen haben:

    JavaScript-Quellcode

    1. var a
    2. var b
    3. var c
    4. console.log(a())
    5. a = function() {
    6. return 1
    7. }
    8. b = function() {
    9. return 1
    10. }
    11. c = function() {
    12. return 1
    13. }
    Alles anzeigen

    Felix K. schrieb:

    geht nicht mit dem => Operator
    Das liegt daran, dass in der Funktion this verwendet wird. this hängt vom Kontext ab. Bei einer Arrow Funktion wird dieser Kontext nicht neu gebunden (wäre in diesem Fall also je nach JS-Version global/window oder undefined) während das function Schlüsselwort den Kontext auf das aufrufende Objekt bindet.


    Eine alternative Schreibweise wäre aber auch noch:

    JavaScript-Quellcode

    1. var counter = {
    2. value: 0,
    3. add(x) {
    4. this.value += x;
    5. return this;
    6. }
    7. }
  • Felix K. schrieb:

    Kannst du bitte ein Beispiel machen was die neue Schreibweise ist oder einen Link schicken? Oder war es das schon?

    Die Schreibweisen für Methoden inklusive der neuen Kurzschreibweisen kannst du hier einsehen. Aber eigentlich war's das schon.


    Felix K. schrieb:

    Wo ist der Unterschied zwischen chaining und piping? Auf die Schnelle hab ich das hier gefunden, meinst du das?
    Ich hatte eigentlich gedacht, ich hatte alles notwendige geschrieben. Den Link hab ich ja auch mit angegeben. Aber für dich noch mal ein Beispiel:

    JavaScript-Quellcode

    1. const numbers = [1, 2, 3, 4, 5]
    2. numbers
    3. .filter(x => x % 2) // [1, 3, 5]
    4. .map(x => x * 2) // [2, 6, 10]
    5. .reduce((a, b) => a + b) // 18

    Hier werden durch die Punktschreibweise verschiedene Array Methoden miteinander "verkettet" (daher "chaining"). Das ursprüngliche numbers Array bleibt hierbei aber unverändert.

    Als Vorbereitung für die nächsten Beispiele werde ich erst mal die Array Methoden zu reinen Funktionen umschreiben:


    JavaScript-Quellcode

    1. function filter(fn, values = []) {
    2. const result = []
    3. for (const value of values) {
    4. if (fn(value)) result.push(value)
    5. }
    6. return result
    7. }
    8. function map(fn, values = []) {
    9. const result = []
    10. for (const value of values) {
    11. result.push(fn(value))
    12. }
    13. return result
    14. }
    15. function reduce(fn, values = []) {
    16. let result
    17. for (const value of values) {
    18. if (result) {
    19. result = fn(result, value)
    20. } else {
    21. result = value
    22. }
    23. }
    24. return result
    25. }
    26. const onlyOdd = filter.bind(undefined, x => x % 2)
    27. const double = map.bind(undefined, x => x * 2)
    28. const add = reduce.bind(undefined, (a, b) => a + b)
    Alles anzeigen
    Sorry, dass das sich das so aufbläst. Ist aber nur zum verbinden der Beispiele (von Objektorientierung zu Funktionsorientierung)(also zum reinen Verständnis).

    Beim piping ist die Ausgabe der vorherigen Funktion die Eingabe der nächsten. Ein schlichtes piping Beispiel wäre also:

    JavaScript-Quellcode

    1. add(double(onlyOdd(numbers))) // 18


    Mit dem Piping Operator (den es noch nicht gibt) bekommt das ganze wieder Ähnlichkeit zum chaining:

    JavaScript-Quellcode

    1. numbers
    2. |> onlyOdd
    3. |> double
    4. |> add


    Eventuell hast du ja auch schon mal mit Streams in Node gearbeitet (z.B. mit Gulp). Dort wird das piping mit der .pipe Methode umgesetzt, so das man dafür chaining einsetzten kann:

    JavaScript-Quellcode

    1. inputStream
    2. .pipe(onlyOdd)
    3. .pipe(double)
    4. .pipe(add)
    5. .pipe(outputStream)
  • Lichtjaeger schrieb:

    const onlyOdd = filter.bind(undefined, x => x % 2)
    Wieso undefined? Ich habe ein ähnliche Beispiel hier gefunden und die Antwort übersetzten lassen, dabei kam das hier raus:


    https://stackoverflow.com/questions/14382682/what-does-it-mean-to-pass-undefined-to-bind - Übersetzt von Google-Translate schrieb:

    Es bedeutet, dass Sie this in der resultierenden gebundenen Funktion nicht auf etwas verweisen möchten . Mit anderen Worten, es stellt sicher, dass, wenn Sie Ihre gebundene Funktion aufrufen, sein this wird undefined. Warum genau Sie das tun, hängt natürlich vom Code ab; viele Funktionen nicht verwenden, this so ist es eine Möglichkeit, aufgeräumt zu sein.
    aber du machst doch gar nichts mit this in den drei Funktion die du dann bindest, oder?
  • Absolut richtig.

    Function.prototype.bind kann man für 2 Dinge einsetzen:
    • Um this manuell zu binden, was ich hier aber nicht brauche und deshalb undefined übergebe
    • Um ein Argument an eine Funktion zu binden
    Ich habe hier die Funktion x => x % 2 (true bei ungeraden Zahlen) als fn-Argument an die allgemeinere filter-Funktion gebunden, um so die speziellere onlyOdd-Filterfunktion zu erzeugen.

    Sinngemäß hätte das auch so aussehen können:

    JavaScript-Quellcode

    1. function onlyOdd(values = []) {
    2. return filter(x => x % 2, values)
    3. }