Funktion mit dynamischer Anzahl an Parameter aufrufen
Dodwin
- javascript
Hallo.
Ich habe folgende Funktion erstellt:
function ajax(file, func_target, param1, param2, param3) {
var tmp_ajax_obj = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
tmp_ajax_obj.open('post', file); // post => no browser-cache
tmp_ajax_obj.onreadystatechange = function(){
if(tmp_ajax_obj.readyState == 4)
if (func_target)
window[func_target](tmp_ajax_obj.responseText, param1, param2, param3);
}
tmp_ajax_obj.send(null);
}
Diese Funktion macht einen Request an die angegebene Adresse (file) und sendet die Antwort an die angegebene Funktion (func_target).
Dabei können derzeit bis zu 3 Parameter übergeben werden, die anschließend an die Zielfunktion weitergereicht werden.
Nun würde ich es aber gerne "sauber" machen und beliebig viele Paramter zulassen.
Ich habe nur leider keine Idee, wie ich das (ohne eval?) lösen könnte.
Verbesserungen an der Funktion sind auch willkommen.
Danke und ein schönes Wochenende,
Dodwin
Hi,
Nun würde ich es aber gerne "sauber" machen und beliebig viele Paramter zulassen.
Ich habe nur leider keine Idee, wie ich das (ohne eval?) lösen könnte.
http://de.selfhtml.org/javascript/objekte/function.htm
MfG ChrisB
Hi.
»» http://de.selfhtml.org/javascript/objekte/function.htm
Da war ich bereits.
Wäre schön, wenn du mir noch sagst, wie mir das weiterhelfen soll...
Das Auslesen der Parameter mittels arguments[x] ist kein Problem (hätte ich vielleicht noch erwähnen sollen).
Aber wie hänge ich diese an die aufzurufende Funktion?
Gruß,
Dodwin
Nun würde ich es aber gerne "sauber" machen und beliebig viele Paramter zulassen.
Man würde dieses Problem in JavaScript nicht so umsetzen. Das ist keine Lösung, die die Sprachfeatures benutzt. Keine Ajax-Bibliothek arbeitet so.
Erst einmal würde man eine Callback-Funktion übergeben, keinen Funktionsnamen, denn das hat viele Vorteile. Da JavaScript eine funktionale Sprache ist und jede Funktion ein vollwertiges Objekt, übergibt man Referenzen auf Funktionsobjekte. Die müssen nicht notwendig global sein, sondern können irgendwo anders hängen, lokal und sogar ohne Namen (anonym) als Funktionsausdrücke notiert sein. So kann man es vermeiden, fünfhundert globale Funktionen zu notieren, sondern kann mit Kapselung und Namespaces arbeiten.
Jetzt ist die Frage, wie man diesen Callback-Funktionen gewisse Variablen zur Verfügung stellt. Auch hier würde ich zu einer funktionale Lösung raten. JavaScript bietet Closures, d.h. verschachtelte Funktionen »erinnern« sich an die lokalen Variablen der Funktion, in der sie notiert wurden:
function request (p1, p2, p3) {
var a = 1, b = 2, c = 3;
ajax('/file.txt', function (response) {
/* Closure, schließt p1, p2, p3, a, b und c ein */
alert(response + "\n" + a + "\n" + b + "\n" + c);
});
}
Wenn du es etwas geordneter willst, kannst du mit Currying arbeiten. Dann kann die Verschachtelung wegfallen:
function handler (a, b, c, response) {
alert(response + "\n" + a + "\n" + b + "\n" + c);
}
function request () {
var a = 1, b = 2, c = 3;
ajax('/file.txt', handler.curry(a, b, c));
}
Dazu bräuchtest du diese Helferfunktion, die alle Funktionsobjekte um die Methode curry erweitert:
Function.prototype.curry = (function (slice) {
return function () {
var func = this, args = slice.apply(arguments);
return function () {
return func.apply(this, args.concat(slice.apply(arguments)));
};
};
})(Array.prototype.slice);
Bei dieser Standard-Currying-Funktion wird der Parameter response hinten drangehangen - man könnte ihn auch vorne anhängen (statt concat die arguments-Liste von hinten durchlaufen und alle Elemente mit unshift in args einfügen).
So wäre es meiner Meinung nach »sauber« - deine gegenwärtige Lösung fühlt sich eher nach PHP an (< 5.3 ;-)).
apply und arguments sind auf jeden Fall die Objekte, die dir hier weiterhelfen. Damit könntest du dein Problem auch lösen, ohne die vorgeschlagenen Änderungen vorzunehmen.
// Wandle arguments-Objekt in einen Array um und extrahiere die Parameter 3-n
var args = Array.prototype.slice.call(arguments, 2);
// Füge vorne einen Parameter ein, nämlich den responseText
args.unshift(tmp_ajax_obj.responseText);
// Rufe Funktion auf mit dem erzeugten Array als Parameterliste
window[func_target].apply(window, args);
Das »Durchreichen« der Parameter funktioniert hier ähnlich wie beim Currying.
Mathias
Hallo molily,
Wow! Vielen Dank für die ausführliche Beschreibung.
Ich werd's mir wohl nochmal in Ruhe durchlesen müssen um alles nachzuvollziehen.
So wäre es meiner Meinung nach »sauber« - deine gegenwärtige Lösung fühlt sich eher nach PHP an (< 5.3 ;-)).
Erwischt... ;)
Gruß,
Dodwin
Hallo molily,
Ok, nach mehrfachem Durchlesen hab ich's jetzt halbwegs verstanden.
In deinem Beispiel ermöglicht das Currying, dass man der Funktionsreferenz auch später noch Parameter anhängen kann, wenn ich das richtig verstanden habe.
Bei dieser Standard-Currying-Funktion wird der Parameter response hinten drangehangen - man könnte ihn auch vorne anhängen (statt concat die arguments-Liste von hinten durchlaufen und alle Elemente mit unshift in args einfügen).
In der curry-Funktion habe ich lediglich die innere return-Zeile geändert:
return func.apply(this, slice.apply(arguments).concat(args));
Das bewirkte schon das gezielte Ergebnis.
Die Ajax-Funktion sieht jetzt so aus:
function ajax(file, func_target) {
var tmp_ajax_obj = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
tmp_ajax_obj.open('post', file); // post => no browser-cache
if (func_target)
tmp_ajax_obj.onreadystatechange = function(){
if(tmp_ajax_obj.readyState == 4)
func_target(tmp_ajax_obj.responseText); // Aufruf der Ziel-Funktion
}
tmp_ajax_obj.send(null);
}
Damit kann ajax() sowohl mit ajax('test.txt',handler.curry(1, 2, 3))
als auch mit
ajax('test.txt', function (resp) {
var a = 1, b =2, c = 3;
// ...
});
aufgerufen werden.
Gruß,
Dodwin