login  Naam:   Wachtwoord: 
Registreer je!
 Tutorials

Tutorials > Javascript


Gegevens:
Geschreven door:
Thomas
Moeilijkheidsgraad:
Moeilijk
Hits:
24104
Punten:
Aantal punten:
 (4.5)
Aantal stemmen:
14
Stem:
Niet ingelogd
Nota's:
 Lees de nota's (0)
 

Tutorial:

Functies in JavaScript

1. Wat is een functie?
2. Hoe is een functie opgebouwd?
3. Wanneer gebruik je functies?
4. Hoe gebruik je een functie?
5. Call by reference en call by value
6. Recursieve functies
7. JavaScript is clientside
8. Voorbeelden
9. Veel voorkomende fouten
10. Tips


1. Wat is een functie?
Een functie is een stuk code dat een bepaalde bewerking (of een reeks van bewerkingen) uitvoert.

top

2. Hoe is een functie opgebouwd?
Een functie ziet er (abstract) als volgt uit:

function <f_naam>([<f_parameter1>[, <f_parameter2>, [...]]]) {
  // pre: <f_pre>
  // post: <f_post> of ret: <f_ret>

  [var <f_lokale_var1>; [var <f_lokale_var2>; [...]]]
  <f_body>
  [return <f_returnwaarde>;]
}

Uitleg:
<...>
Een naam die (meestal) een variabele voorstelt.

<f_naam>
De naam die je aan de functie geeft. Het is verstandig om de functie een naam te geven die omschrijft wat de functie doet.

[...]
Variabelen die tussen rechte haken staan zijn optioneel - deze hoeven niet per sé in een functie voor te komen.
Een functie hoeft dus geen parameters, lokale variabelen of return-value te hebben (zie hieronder voor uitleg).

<f_parameter1>, <f_parameter2>, ...
Deze variabelen vormen de invoer van je functie.
Als je invoerparameters nodig hebt, omdat je bijvoorbeeld iets wilt berekenen dan kun je hier voor elke variabele die je denkt nodig te hebben een (parameter)naam invoeren. Deze parameters zijn een soort van dummy-variabelen - bij de _declaratie_ van de functie gebruik je deze variabelen om aan te geven wat er dient te gebeuren met de bijbehorende waarden binnenin de functie. Als je een functie _aanroept_ gebruik je concrete waarden (of objectreferenties) in plaats van parameter-namen.

Vergelijk:

// functie declaratie:
function som(a, b) {
  // pre: a en b zijn twee positieve gehele getallen
  // ret: de som van a en b

  // retourneer het resultaat
  return(a+b);
}

// functie aanroep:
alert(som(1,2));

(er verschijnt een popup met de waarde "3" in beeld)

Globale variabelen (variabelen die buiten functies staan gedeclareerd) zijn binnen een functie ook 'bekend'. Het is beter om bij functies hiervan GEEN gebruik te maken, omdat dit verwarrend werkt. Probeer er een gewoonte van te maken om alle variabelen die een functie nodig heeft mee te geven aan de functie in de vorm van parameters. Probeer ook te vermijden dat globale variabelen dezelfde naam hebben als variabelen die binnenin een functie worden gebruikt.

Voorbeeld:

function huh1(test) {
  test = test+1;
}

function huh2() {
  test = test+1;
}

// globale variabele 'test'
var test = 1;

huh1(test);
alert(test);
// levert 1

huh2();
alert(test);
// levert 2 !!!

Geef variabelen die je in een functie wilt gebruiken mee aan de functie (of declareer ze binnen de functie), maar maak in dit geval géén gebruik van het feit dat de functie de variabele 'toch wel kent'. In kleine scripts is deze manier van werken misschien nog wel te volgen, maar wanneer je script groter wordt, en variabelen op meerdere plaatsen van waarde veranderen wordt dit hopeloos ingewikkeld.

//
Als je twee slashes aan het begin van de regel zet, word de rest van de regel als commentaar beschouwd. Hier doet JavaScript dus verder niets mee. De rest van de regel kun je dus gebruiken om je code toe te lichten.

<f_pre> en <f_post> of <f_ret>
Deze onderdelen zijn niet verplicht (het is commentaar), maar het is een goede gewoonte om te omschrijven wat een functie precies doet.
<f_pre> geeft aan wat er _voor_ de aanroep van een functie moet gelden (préconditie) - hier kun je ook eventueel een omschrijving geven van wat de parameters betekenen en wat voor waarden ze zouden moeten bevatten.
<f_post> geeft aan wat er na afloop van de aanroep van een functie geldt / zou moeten gelden (postconditie).
Soms word in plaats van '// post:' '// ret:' gebruikt wanneer de functie een waarde retourneert.
Het voordeel van het op deze manier specificeren van functies is, dat het er niet toe doet hoe je een functie schrijft (implementeert), als er maar aan de pre- en post-/ret-conditie is voldaan - je zou dan de implementatie van de functie elk moment kunnen veranderen.

var <f_lokale_var1>; var <f_lokale_var2>; ...
Wanneer je tijdens het uitvoeren van een functie extra variabelen nodig hebt voor het doen van bewerkingen op andere variabelen / parameters, kun je deze (bij voorkeur aan het begin van de functie) declareren. Het is hier wederom verstandig om omschrijvende namen te geven. Het gebruik en de precieze betekenis van deze variabelen kun je weer met behulp van commentaar toelichten.
Lokaal (binnen de functie-declaratie) gedeclareerde variabelen bestaan alleen binnen de functie. Je kan ertoe besluiten om bij de declaratie van lokale variabelen deze meteen een initiële waarde te geven; Je hoeft dan alleen de waarde van deze variabele te veranderen als dit nodig is - en dit scheelt meestal wat code.

Vergelijk:

function groter_dan_2(getal) {
  // pre: getal is een geheel getal
  // ret: de functie geeft een boolean terug die aangeeft of getal groter is dan 2

  var groter;
  if(getal > 2) {
    groter = true;
  } else {
    groter = false;
  }

  return groter;
}

met:

function groter_dan_2(getal) {
  // pre: getal is een geheel getal
  // ret: de functie geeft een boolean terug die aangeeft of getal groter is dan 2

  var groter = false;
  if(getal > 2) {
    groter = true;
  }

  return groter;
}

Het kan altijd nog korter (je hebt niet per sé een lokale variabele nodig):

function groter_dan_2(getal) {
  // pre: getal is een geheel getal
  // ret: de functie geeft een boolean terug die aangeeft of getal groter is dan 2

  return(getal > 2);
}

Dit is een mooi voorbeeld waarbij gebruik gemaakt wordt van de pré- en post- (of ret-)conditie:
De in- en uitvoer blijft hetzelfde, terwijl de functie-body continu verandert.

<f_body>
Hier kun je code neerzetten die de taken uitvoert waarvoor de functie geschreven is. Bijvoorbeeld: tel twee getallen bij elkaar op, druk een tekst af, sorteer een array et cetera.

return <f_returnwaarde>;
Wanneer je je functie hebt uitgevoerd, en je hebt de resultaten van de functie opgeslagen in een of andere lokale variabele, kun je het resultaat van de functie-aanroep 'teruggeven' met behulp van het return-statement, gevolgd door de naam van de lokale variabele.
Voor de returnwaarde kun je ook een boolse waarde nemen. Wanneer bijvoorbeeld alle onderdelen van een functie succesvol zijn uitgevoerd (er is aan de pré- en postconditie voldaan) kun je de waarde 'true' teruggeven - wanneer niet alle onderdelen van de functie zijn uitgevoerd kun je 'false' retourneren.
Als een functie gekoppeld is aan een event, bijvoorbeeld aan een onSubmit-event van een formulier, zal 'return false;' er voor zorgen dat het formulier _niet_ gesubmit wordt:

// functie declaratie
function check(form) {
  // pre:  form is het te controleren formulier
  // post: een boolean die aangeeft of het veld 'comment' inhoud heeft

  var heeft_inhoud = false;

  if(form.elements['comment'].value == "") {
    alert("Type wat in !");
  } else {
    heeft_inhoud = true;
  }

  return heeft_inhoud;
}


Bijbehorend formulier:

<form action="somepage.htm" method="post" onSubmit="return check(this);">
comments here: <input type="text" name="comment" /><br />
<input type="submit" value="submit" />
</form>

Het is een goede ontwerpgewoonte je functies zo te maken, dat deze maar één return-statement hebben (meerdere return-statements zijn in principe mogelijk).

top

3. Wanneer gebruik je functies?
Als je vaak dezelfde bewerking moet uitvoeren, is het meestal de moeite waard om hiervoor een functie te introduceren. Ook wanneer je een (aantal) complexe bewerking(en die samen een logisch geheel vormen) moet uitvoeren kun je besluiten hiervoor een functie te maken.

Het is niet echt zinnig om voor bestaande functionaliteit een compleet nieuwe functie te introduceren:

function drukaf(tekst) {
  //pre:  'tekst' bevat een tekst die afgedrukt dient te worden
  //post: 'tekst' is afgedrukt op het scherm

  document.write(tekst);
}

Men moet waken voor een wildgroei van dit soort functies.
Functies dienen een zekere meerwaarde te hebben.

top

4. Hoe gebruik je een functie?
definieren
Voordat je een functie kan gebruiken (aanroepen) dien je deze te declareren. Je vertelt wat de functie doet op de manier die hierboven staat beschreven.

aanroepen
Hierna kun je de functie gebruiken waar je deze nodig hebt.

geretourneerde waarden gebruiken
Je kan het resultaat van een functie ook weer toekennen aan een variabele buiten de functie.
Gebruikmakend van de eerdergenoemde som-functie:

// ...
// hiervoor staat ergens de functie som() gedeclareerd
var resultaat = som(1,2);
// de variabele 'resultaat' bevat nu de waarde 3.

top

5. Call by reference en call by value
Als je globale variabelen (die zijn gedeclareerd met 'var') meegeeft aan een functie, dan kun je van de waarden van deze variabelen gebruik maken. Je kan echter deze variabelen _niet_ van waarde doen laten veranderen binnen deze functie.

Voorbeeld:

var tekst = "Dit is een tekst.";

function verander_tekst(txt) {
  txt = "Dit is een andere tekst.";
}

verander_tekst(tekst);
alert(tekst);
// levert "Dit is een tekst."

Dit komt omdat er alleen maar naar de waarde van de meegegeven variabele (parameter) gekeken wordt. Je kan met deze waarde werken, maar deze niet aanpassen.
Dit wordt "call by value" genoemd - elke gewone variabele die je in JavaScript aan een functie voert, heeft de eigenschap dat deze niet van waarde veranderd kan worden, met andere woorden: deze is (binnen de functie) constant. Soms is een parameter gewoon een getal, boolean of een string die niet afkomstig is van een variabele. Je kan de "variabele" 1 niet van waarde doen veranderen (zie ook de aanroep van de eerdergenoemde som-functie, hier word geen gebruik gemaakt van aparte (globaal gedeclareerde) variabelen).

Wat je wel kunt doen is de aangepaste waarde retourneren en deze dan aan de variabele toekennen waarmee de functie werd aangeroepen:

var tekst = "Dit is een tekst.";

function verander_tekst(txt) {
  txt = txt+" Aangepast.";
  return txt;
}

tekst = verander_tekst(tekst);
alert(tekst);
// levert "Dit is een tekst. Aangepast."

JavaScript heeft ook de beschikking over objecten. Wanneer je een object aan een functie voert, dan word er een referentie van dit object aan de functie meegegeven. Dit heet "call by reference".
(attributen van) objecten kunnen wel van waarde veranderen.

Voorbeeld:

function stack_push(arr, val) {
  // pre:  arr is een Array-object
  // post: de waarde van val is toegekend aan de eerste vrije positie van arr

  arr[arr.length] = val;
}

// declaratie van (globaal) Array-object
var arr = new Array("a","b","c");

stack_push(arr, "d");
alert(arr.join(";"));
// levert "a;b;c;d"

In dit voorbeeld is dus de parameter 'arr' call-by-reference, en 'val' call-by-value.

top

6. Recursieve functies
Sommige functies roepen zichzelf aan. Dit zijn recursieve functies. Je moet hier heel goed op twee dingen letten: voortgang en eindiging.
Voortgang wil zeggen dat een functie iets 'moet blijven doen', maar het moet ook weer niet zo zijn dat een functie iets _eindeloos_ moet blijven doen, hij moet een keer eindigen. Er moet dus iets in de functie zitten / aan de functie worden meegegeven dat bij elke functie-aanroep afneemt zodat je op een bepaald punt kunt vaststellen dat je functie 'klaar' is. Gegevens (die je aan de functie voert) zijn altijd 'eindig' van aard. Je zou dus met behulp van eigenschappen van deze gegevens kunnen bepalen wanneer je klaar bent met het uitvoeren van een (recursieve) functie.
Iets waar je dus goed op moet letten bij het maken recursieve functies, is dat je functies niet in een oneindige lus terecht komen (vooral als je alert's gebruikt om te debuggen), anders kun je in de meeste gevallen je niet-reagerende browser afsluiten als het mis gaat :].

Voorbeeld:

// declaratie
function printboom(tree, depth) {
  // pre: tree is een array met af te drukken items, depth is het aantal spaties*2,
  //      dat voor elk array-item wordt ingesprongen
  // post: alle items van tree zijn afgedrukt

  for(i in tree) {
    // controleer of het huidige item een array is
    if((typeof tree[i]) == "object") {
      // het betreft een array, ga een niveau dieper
      printboom(tree[i], depth+1);
    } else {
      // het betreft een array-item, druk het af
      for(j=0; j < depth; j++) {
        document.write("&nbsp;&nbsp;");
      }
      document.write(tree[i]+"<br />");
    }
  }
}

// declaratie van boom-array
var boom = new Array("1", new Array("1.1", "1.2", new Array("1.2.1", "1.2.2")), "2", new Array("2.1", "2.2"));

// aanroep
printboom(boom, 0);

Levert:

1
  1.1
  1.2
    1.2.1
    1.2.2
2
  2.1
  2.2


top

7. JavaScript is clientside
JavaScript wordt uitgevoerd op de machine die een pagina op het internet opvraagt die JavaScript bevat. Je zou JavaScript dus ook goed kunnen gebruiken bij het besparen van bandbreedte door functies te schrijven voor het afdrukken van bijvoorbeeld tabel-rijen. Dit heeft trouwens alleen zin als je tabellen behoorlijk lang zijn / kunnen worden.

Vergelijk:

<table border="1">
<tr>
<td>band</td>
<td>album</td>
<td>kwaliteit (kbps)</td>
</tr>

<tr>
<td>Deftones</td>
<td>Adrenaline</td>
<td>160</td>
</tr>

<tr>
<td>Deftones</td>
<td>White Pony</td>
<td>160</td>
</tr>

<tr>
<td>Dropkick Murphy's</td>
<td>Sing Loud, Sing Proud!</td>
<td>160</td>
</tr>
</table>

Versus:

// functie declaratie
function rij(band, album, kwaliteit) {
  // drukt een rij van de muziek-tabel af
  document.write("<tr><td>"+band+"</td><td>"+album+"</td><td>"+kwaliteit+"</td></tr>");
}

<table border="1">
<tr>
<td>band</td>
<td>album</td>
<td>kwaliteit (kbps)</td>
</tr>
<script language="JavaScript">
<!--
rij("Deftones", "Adrenaline", 160);
rij("Deftones", "White Pony", 160);
rij("Dropkick Murphy's", "Sing Loud, Sing Proud!", 160);
//-->
</script>
</table>

Hiernaast zou je meer functies kunnen toevoegen waarmee je de opmaak van de inhoud regelt. De JavaScript zou op zijn beurt weer gegenereerd kunnen worden door een serverside scripting taal, zoals PHP. Hoe meer rijen je toevoegt, hoe meer ruimte (bandbreedte bij versturen) het relatief gezien scheelt.

top

8. Voorbeelden

top

9. Veel voorkomende fouten
  • JavaScript is case-sensitive en strict in de syntax, hierdoor zijn er veel fouten mogelijk...
top

10. Tips
  • probeer alles wat je in een functie wilt gebruiken als parameter mee te geven - dit hoef je in principe niet te doen als de variabele globaal gedeclareerd staat, maar dit werkt _zeer_ verwarrend
  • voorzie functies van een pré- en post- (of ret-)conditie
  • declareer lokale variabelen aan het begin van de functie
  • gebruik commentaar in je code om duidelijk te maken wat er gebeurt (of zou moeten gebeuren)
  • schrijf je functies zo, dat je maximaal één return-statement hebt
  • spring bij lussen in in je code, om het geheel leesbaar te houden
top



Volgende tutorial : Lussen »

© 2002-2024 Sitemasters.be - Regels - Laadtijd: 0.018s