Globale variable?
WernerK
- javascript
Hallo
sicher ist es für euch Experten zum lachen.
In einer html Seite rufe ich eine Javascript Funktion auf und übergebe eine ID. Im Beispiel ist diese 1.
onload=" getStatus(1);
Über einen Ajax Request werden Daten von einem Webserver geladen.
Dies soll immer wieder gemacht werden. Daher habe ich einen Refresh eingebaut.
Mein Problem ist nun das ich in
setTimeout('getStatus(2)',5000);
die ID fest drin haben möchte . Sie soll gleich sein wie am Anfang.
Natürlich könnte ich hier nun auch die 1 fest drin lassen.
Meine Frage ist nun wie kann ich das "flexibel" machen?
Ich dachte mit "id = id;" in der getStatus Funktion mache ich die ID global?
Der Alter test bei handleResponseStatus bringt aber nur undefined.
Und ich kann ja auch keinen Parameter übergeben wie wie getStatus(1)
Kann jemand helfen?
function getStatus(id) {
id = id;
xmlhttp1=GetXmlHttpObject();
if (xmlhttp1==null){
alert ("Browser does not support HTTP Request");
return;
}
var url="http://localhost/get_status.php";//
//this is needed because of window refresh
var rand = Math.ceil(Math.random()*100000);
url=url+"?id="+id+"&ref="+rand;
xmlhttp1.onreadystatechange = handleResponseStatus;
xmlhttp1.open("GET",url,true);
xmlhttp1.send(null);
}
function handleResponseStatus(){
if (xmlhttp1.readyState==4){
document.getElementById("gadgetContent").innerHTML=xmlhttp1.responseText;
alert(id);
setTimeout('getStatus(2)',5000);
}
}
Gruss
Werner
Natürlich könnte ich hier nun auch die 1 fest drin lassen.
Und warum machst du das?
Meine Frage ist nun wie kann ich das "flexibel" machen?
Ich dachte mit "id = id;" in der getStatus Funktion mache ich die ID global?
Das hat genau keinen Sinn.
Ich verstehe deine intention nicht - willst du 1 als standardwert haben und das ganze optional mit einem anderen Wert überschreiben? Oder beim 1. Aufruf 1 und sonst immer 2?
Hi suit,
Ich verstehe deine intention nicht - willst du 1 als standardwert haben und das ganze optional mit einem anderen Wert überschreiben? Oder beim 1. Aufruf 1 und sonst immer 2?
Ich möchte gerne den Aufruf flexibel machen. Also wenn ich in der HTML Datei
onload=" getStatus(1); angebe, soll dies dann auch für die ID funktionieren.
Wenn ich onload=" getStatus(2); mache soll es für die ID 2 gehen.
Bisher hatte ich in "setTimeout('getStatus(2)',5000);" Funktionen die ID fest drin.
Mir ist nur nicht klar wie ich die id in die zweite funktion bekomme;
setTimeout('getStatus(id)',5000);
also
function handleResponseStatus(){
if (xmlhttp1.readyState==4){
document.getElementById("gadgetContent").innerHTML=xmlhttp1.responseText;
alert(id);
setTimeout('getStatus(id)',5000);
}
}
Mahlzeit WernerK,
Mir ist nur nicht klar wie ich die id in die zweite funktion bekomme;
setTimeout('getStatus(id)',5000);
Ganz einfach wie beschrieben - beachte das "<http://de.selfhtml.org/javascript/objekte/window.htm#set_timeout@title=Beachten Sie:>" ...
Alternativ könntest Du natürlich auch statt eines Strings, der einen Funktionsaufruf enthält, auch eine entsprechende Funktionsreferenz übergeben.
MfG,
EKKi
Hallo EKKi,
Ganz einfach wie beschrieben - beachte das "<http://de.selfhtml.org/javascript/objekte/window.htm#set_timeout@title=Beachten Sie:>" ...
Alternativ könntest Du natürlich auch statt eines Strings, der einen Funktionsaufruf enthält, auch eine entsprechende Funktionsreferenz übergeben.
Hmm, verstehe ich nun icht ganz,
Das Problem ist doch das in der Funktion
handleResponseStatus(){
schon die variable "id" nicht bekannt ist.
alert(id);
bringt undefined
Mahlzeit WernerK,
Hmm, verstehe ich nun icht ganz,
Das Problem ist doch das in der Funktion
handleResponseStatus(){
schon die variable "id" nicht bekannt ist.
Und was spricht dagegen, dieser Funktion den jeweiligen Wert der Variable "id" mitzugeben?
xmlhttp1.onreadystatechange = function() {
handleResponseStatus(id);
};
Dann könntest Du in der Funktion "handleResponseStatus()" einfach
setTimeout('getStatus(' + [ref:self812;javascript/objekte/function.htm#arguments@title=arguments][0] + ')', 5000);
bzw.
setTimeout(function() {
getStatus([ref:self812;javascript/objekte/function.htm#arguments@title=arguments][0]);
}, 5000);
schreiben ... oder Du passt die Funktionsdeklaration entsprechend an, um ein benanntes Funktionsargument zu erhalten.
MfG,
EKKi
Hallo,
Und was spricht dagegen, dieser Funktion den jeweiligen Wert der Variable "id" mitzugeben?
Ja, genau.
Aber warum bemühst du das arguments
-Array?
setTimeout('getStatus(' + [ref:self812;javascript/objekte/function.htm#arguments@title=arguments][0] + ')', 5000);
Es sollte doch auch einfach so gehen:
setTimeout(function(){getStatus(id);}, 5000);
Denke, die Verwendung von arguments
ist möglichst zu vermeiden und verlangsamt eine Funktion enorm, auch wenn das im Fall hier nicht wirklich wichtig scheint.
Gruß, Don P
Mahlzeit Don P,
Aber warum bemühst du das
arguments
-Array?
Die Alternative hatte ich genannt ("oder Du passt die Funktionsdeklaration entsprechend an, um ein benanntes Funktionsargument zu erhalten.") ...
Es sollte doch auch einfach so gehen:
setTimeout(function(){getStatus(id);}, 5000);
Nur wenn - wie gesagt - die Funktionsdeklaration um einen entsprechenden Parameter erweitert wird.
MfG,
EKKi
xmlhttp1.onreadystatechange = function() {
handleResponseStatus(id);
};
Hier wendest du doch schon ein Pattern an, was man bei setTimeout ebenfalls verwenden kann.
> Dann könntest Du in der Funktion "handleResponseStatus()" einfach
>
> `setTimeout('getStatus(' + [ref:self812;javascript/objekte/function.htm#arguments@title=arguments][0] + ')', 5000);`{:.language-javascript}
>
> bzw.
>
> ~~~javascript
setTimeout(function() {
> getStatus([ref:self812;javascript/objekte/function.htm#arguments@title=arguments][0]);
> }, 5000);
Diese Beispiele sind nicht äquivalent. Die verschachtelte Funktion erbt nicht das arguments-Objekt der darüberliegenden, sondern hat ein eigenes. Und die verschachtelte Funktion bekommt keine Parameter, es sei denn, setTimeout bekommt weitere Parameter.
Die verschachtelte Funktion ist allerdings eine Closure und hat damit Zugriff auf die normalen Variablen der darüberliegenden Funktion. Daher ist ein einfaches setTimeout(function () { getStatus(id); }, 1000);
möglich, wie Don P sagt.
Mathias
Mahlzeit molily,
Daher ist ein einfaches
setTimeout(function () { getStatus(id); }, 1000);
möglich, wie Don P sagt.
Nur wenn die Funktionsdeklaration um einen entsprechenden Parameter erweitert wird, wie ich bereits sagte.
MfG,
EKKi
Hallo,
Daher ist ein einfaches
setTimeout(function () { getStatus(id); }, 1000);
möglich, wie Don P sagt.Nur wenn die Funktionsdeklaration um einen entsprechenden Parameter erweitert wird, wie ich bereits sagte.
Klar, davon bin ich bei meiner Antwort doch ausgegangen nach der Bemerkung "Ja genau". Sonst klappt's natürlich nicht.
Gruß, Don P
Hallo!
Meine Frage ist nun wie kann ich das "flexibel" machen?
Ich dachte mit "id = id;" in der getStatus Funktion mache ich die ID global?
Flexibel machst du JavaScript NICHT, wenn du anfängst, globale Variablen einzuführen. Globale Variablen solltest du möglichst vermeiden, zumal solche mit generischen Namen wie »id«.
Wenn bereits eine Funktionsvariable id existiert (was der Fall ist, wenn sie in der Parameterliste deklariert ist), so legt die Zuweisung id = id;
in einer Funktion keine globale Variable an. Da müsstest du schon explizit window.id = id;
schreiben. Doch wie gesagt solltest du davon absehen.
Es kommt in JavaScript häufig vor, dass man verschiedene asynchrone Funktionen hat, die untereinander auf gemeinsame Daten zugreifen sollen. Das passiert insbesondere bei Event-Handlern, Callbacks sowie bei setTimeout/setInterval.
Es gibt verschiedene Möglichkeiten, wie sich der gemeinsame Zugriff auf gewisse Variablen ermöglichen lässt, OHNE globale Variablen anzulegen. Eine gute Idee ist, erst einmal alle Daten in einem Objekt zu gruppieren, z.B. in Modulen oder Instanzen. Dann sorgt man dafür, dass Methoden in deren Kontext aufgerufen werden, man also mit this
auf das gemeinsame Objekt zugreifen kann. Hierfür lassen sich Closures verwenden oder explizites Binding von Funktionen.
Angenommen, wir benutzen ein Object-Literal zur Gruppierung und nutzen dann Function.prototype.bind zum Function-Binding:
// Object-Literal, der alle Daten gruppiert
var StatusUpdater = {
// Konfiguration - hier steht alles flexible
id : 1,
url : "http://localhost/get_status.php",
targetElementId : 'gadgetContent',
interval : 5000,
// Platzhalter-Eigenschaften, werden im Laufe gefüllt
targetElement : null,
xhr : null,
targetElement : null,
// Methoden
getXHR : function () {
return new XMLHttpRequest();
},
getStatus : function () {
// Rufe StatusUpdater-Methode auf und speichere Rückgabe in StatusUpdater-Eigenschaft
this.xhr = this.getXHR();
if (!this.xhr){
alert ("Browser does not support HTTP Request");
return;
}
var rand = Math.ceil(Math.random() * 100000);
url = url + "?id=" + id + "&ref=" + rand;
// Erzeuge eine an StatusUpdater gebundene Funktion, nutze diese als Event-Handler
this.xhr.onreadystatechange = this.handleResponse.bind(this);
this.xhr.open("GET", this.url, true);
this.xhr.send(null);
},
handleResponseStatus : function () {
if (this.xhr.readyState != 4) return;
if (!this.targetElement) {
this.targetElement = document.getElementById(this.targetElementId);
}
this.targetElement.innerHTML = this.xhr.responseText;
// Erzeuge eine gebundene Funktion, nutze diese für setTimeout
setTimeout(this.getStatus.bind(this), this.interval);
}
};
(ungetestet, es geht ums Prinzip)
Anstatt den vielen »this.« könnte man auch »StatusUpdater.« schreiben. Aber eigentlich sollten die Funktionen nicht darauf festgelegt sein, in dessen Kontext aufgerufen zu werden. Ändert man mal den Namen, kopiert Funktionen um oder verwendet sie wieder, so wäre der Aufwand enorm. Deswegen ist die Nutzung des this-Kontextes schon sinnvoll.
Um sich ein wenig Tipparbeit zu sparen und den Code übersichtlicher zu machen, kann man für häufig verwendete Variablen lokale Shortcuts verwenden, z.B. var xhr = this.xhr;
.
Allgemein gesagt sind Funktionen, lokale Variable und Verschachtelung von Funktionen das Mittel der Wahl, um Daten zu kapseln und gleichzeitig zwischen verschiedenen Funktionen zu teilen. Siehe auch Closures.
Mathias
Lieber molily,
habe ich da etwas missverstanden? Wo kommen in StatusUpdater.getStatus() die Variablen "id" und "url" plötzlich her? Sollten die nicht zuerst als lokale Variablen deklariert werden, oder wolltest Du gleich auf StatusUpdater.id und StatusUpdater.url zugreifen?
url = url + "?id=" + id + "&ref=" + rand;
Liebe Grüße,
Felix Riesterer.
Stimmt, es sollte
var url = this.url + "?id=" + this.id + "&ref=" + rand;
lauten, und dann diese Variable verwenden:
this.xhr.open("GET", url, true);
Mathias
Hallo Mathias,
vielen Dank für die tolle Erklärung und Mühe die du dir machst.
Ich muss zugeben das ich nicht alles verstanden habe :-)
Es ist aber ein interessanter Ansatz.
So wie ich es verstehe ist dann alles in einem Objekt "StatusUpdater"
Die ganzen Funktionen, der Ajax Request und die Variablen.
Wie aber rufe ich dann dies aus der ursprünglichen HTML Seite mit onload= ?
auf?
Gruss
werner
Wie aber rufe ich dann dies aus der ursprünglichen HTML Seite mit onload= ?
Ups, das habe ich vergessen.
Von außen kannst du über StatusUpdater.url
, StatusUpdater.getStatus()
usw. auf Eigenschaften und Methoden des Objekts zugreifen.
<body onload="[code lang=javascript]StatusUpdater.getStatus()
">[/code]
oder besser in JavaScript (wieder mit bind
, damit this in getStatus
auf StatusUpdater
zeigt):
window.onload = StatusUpdater.getStatus.bind(StatusUpdater);
oder besser mit einer addEvent-Helferfunktion, damit man irgendwann mehrere Event-Handler registrieren kann:
addEvent(window, 'load', StatusUpdater.getStatus.bind(StatusUpdater));
Anstelle dieses ständigen wiederholten Bindings kann man Funktionen einmal binden und dann StatusUpdater.getStatus
mit der gebundenen Funktion überschreiben, z.B.
StatusUpdater.getStatus = StatusUpdater.getStatus.bind(StatusUpdater);
Damit ist garantiert, dass this
darin immer auf StatusUpdater
zeigt, auch wenn ich die Funktion als Event-Handler verwende oder setTimeout
übergebe.
Mathias