Hoofdstuk 2 De taal Javascript

2.1 Javascript leren

Er zijn talloze goede cursussen en andere bronnen waarmee je Javascript kunt leren. Deze syllabus is dan ook geen cursus Javascript, maar richt zich vooral op Javascript voor muziektechnologen.

2.2 Javascript bronnen

Als HKU-student heb je toegang tot de cursussen van Lynda.com. Hier vind je diverse goede Javascript tutorials. Je kunt inloggen met je HKU-account.

Javascript op Lynda.com

NB: Deze is erg goed om mee te beginnen : Introducing the browser console

Node.js op Lynda.com

w3schools.com tutorials

csdhku op Github

2.3 Flow control

Met Javascript heb je diverse mogelijkheden om het verloop van je programma te bepalen. Die zijn in te delen in twee groepen: beslissingen en herhaling.

2.3.1 Beslissingen (conditions)

Beslissingen (Engels: conditions) bepalen of een stuk code wel of niet uitgevoerd wordt, afhankelijk van het optreden van een bepaalde situatie. Men zegt ook wel: als er "aan een bepaalde conditie voldaan wordt" of dat bepaalde code "conditioneel wordt uitgevoerd".

2.3.1.1 if, else

Dit is de eenvoudigste vorm van een beslissing: als de conditie waar is wordt de code tussen { en } uitgevoerd en anders gebeurt er niks.

if(true) {
  console.log("Dit is altijd waar");
}
 
if(false) {
  console.log("Dit wordt nooit uitgevoerd");
}

In het volgende geval wordt als a > b de regel "a is groter dan b" geschreven. Als a niet groter is dan b, dan gebeurt er niks.

if(a > b) {
  console.log("a is groter dan b");
}

Je kunt ook iets doen als a niet groter is dan b, dat zet je dan tussen de {} haakjes na het 'else' statement:

if(a > b) {
  console.log("a is groter dan b");
}
else {
  console.log("a is kleiner dan b of gelijk aan b");
}

2.3.1.2 Meerdere condities (AND/OR)

Als je wilt aangeven dat meerdere dingen tegelijk waar moeten zijn dan gebruik je AND, maar in Javascript schrijf je dat als &&

if(x>0 && x<10) console.log("x is groter dan 0 EN kleiner dan 10 dus x ligt tussen 0 en 10");

Als je wilt aangeven dat van meerdere dingen er maar eentje waar hoeft te zijn dan gebruik je OR, maar in Javascript schrijf je dat als ||

if(x<0 || x>10) console.log("x is kleiner dan 0 of groter dan 10");

2.3.1.3 switch

Met een switch kun je een keuze maken uit verschillende mogelijkheden. Kijk eerst eens naar het volgende stukje code:

if(huisnummer == 1) {
  console.log("Hier woont mevrouw Jansen");
}
else if(huisnummer == 3) {
  console.log("Hier woont de familie de Boer");
}
else if(huisnummer == 5) {
  console.log("Dit is een spookhuis");
}
else {
   console.log("Dit huisnummer is onbekend");
}

Met een switch ziet dat er veel overzichtelijker uit en het is bovendien efficienter omdat niet telkens de waarde van huisnummer hoeft te worden opgehaald en vergeleken:

switch(huisnummer) {
   case 1: console.log("Hier woont mevrouw Jansen");
   break;
   case 3: console.log("Hier woont de familie de Boer");
   break;
   case 5: console.log("Dit is een spookhuis");
   break;
   default:
   console.log("Dit huisnummer is onbekend");
}

2.3.2 Herhaling (loops)

Herhalingen (in 't Engels: loops) zijn stukjes code die herhaaldelijk uitgevoerd kunnen worden, tot er aan een bepaalde conditie voldaan wordt.

2.3.2.1 while

let teller = 0;
while(teller < 10) {
  console.log("teller: " + teller);
  teller++;
}

2.3.2.2 do...while

let teller = 0;
do {
  console.log("teller: " + teller);
  teller++;
} while(teller < 10);

Het verschil tussen while(cond){...} en do{...}while(cond) zit erin dat de code binnen de {} haakjes van do{...}while(cond) altijd minimaal 1 keer wordt uitgevoerd en bij while(cond){...} kan het ook gebeuren dat het helemaal niet wordt uitgevoerd. Dat ligt aan het punt waarop de beslissing wordt genomen om te stoppen met de loop. Bij while(){...} is dat al aan het begin en bij do{...}while() pas aan het eind, dus wanneer de code al een keer is uitgevoerd.

2.3.2.3 for

Tussen de haakjes van een for-loop staan drie stukjes Javascript-code:

for(initialisatie; conditie; increment) { doe iets }

Met één iteratie bedoelen we het eenmalig uitvoeren van alle code die staat tussen de { en } na het for-statement.

Een veel voorkomende toepassing van 'for' is een teller te maken die een aantal waarden doorloopt:

for(let teller=0; teller < 10; teller++) {
  console.log( "teller: " + teller);
}

Merk op dat de variabele teller alleen binnen de loop bestaat.

2.3.2.4 break, continue

De break hadden we al gezien in de switch. Een ander voorbeeld:

let teller = 0;
 
while(teller < 10) {
  console.log("teller: " + teller);
  teller++;
  if(teller==5) break;
}

2.4 Functies

2.4.1 Waarom functies (1): ze verhogen het overzicht

Wanneer je een programma schrijft in een programmeertaal als C++, PHP of Javascript dan kun je alle code onder elkaar in een grote file zetten en al die code vervolgens van boven tot beneden door de computer laten uitvoeren.

Voor een klein programmaatje gaat dat vaak nog wel, maar als je meer dan een pagina code schrijft wordt het al gauw onoverzichtelijk. In dat geval loont het de moeite om gedeelten uit je code die functioneel gezien bij elkaar horen in aparte brokken te stoppen. Zo'n brok geef je een naam die aangeeft wat het doet en je hebt daarmee de eenvoudigste vorm van een functie gemaakt.

Die functie kun je dan vanuit het hoofdprogramma aanroepen, waardoor je in het hoofdprogramma duidelijk de verschillende stappen in je programma kunt onderscheiden.

2.4.2 Waarom functies (2): hergebruik van stukken code

Als je een programma schrijft waarin meerdere keren ongeveer hetzelfde gebeurt dan is het vaak lonend om uit te zoeken of die herhaling altijd exact hetzelfde is en zo niet, op welke punten het verschilt.
In het eerste geval kun je de code die telkens herhaald wordt in een functie opnemen zodat je het maar 1 keer hoeft te maken en je kunt die functie dan zo vaak aanroepen als je wilt.
In het tweede geval ga je op zoek naar de grootste gemene deler van het stuk code dat met kleine verschillen telkens herhaald wordt. Goede kans dat je die code in een functie kunt stoppen en bij het aanroepen aangeven wat er anders is dan de vorige keer dat je de functie aanriep. Hoe je dat dan aangeeft bespreken we later.

2.4.3 Waarom functies (3): gebruik door anderen

Als programmeur kun je je lekker uitleven en mooie software schrijven waar je heel erg trots op bent en die jij helemaal begrijpt. Wanneer iemand anders met jouw software moet werken of jij wilt een stuk software van iemand anders gebruiken, dan zul je zien dat het niet meevalt om software uit te wisselen. Onderschat ook niet dat je over een maand alweer veel vergeten bent van wat je nu bedenkt, dus zorg dat je je eigen code over een tijdje nog begrijpt.

Programmeurs zijn in het algemeen vrij eigenwijs en hebben vaak een eigen stijl van programmeren. De een gebruikt HoofdLetters Voor AlleVariabelen, de ander gebruikt liever underscores. De plaatsing van haakjes en het gebruik van tabs en spaties is ook vaak een kwestie van voorkeur en zo zijn er allerlei redenen om de code van iemand anders als slecht leesbaar te bestempelen.

Verder heb je niet altijd de tijd om je te verdiepen in alle details van andermans software maar wil je het gewoon gebruiken zonder te weten hoe het werkt.

Hier komen functies weer om de hoek kijken. Maak je nette functies die een bepaald gedeelte van jouw programma uitvoeren dan hoef je aan anderen alleen maar te vertellen hoe ze die functie moeten aanroepen om jouw functionaliteit in hun eigen programma te gebruiken. Je geeft als het ware een stuk gereedschap en vertelt erbij hoe je het moet gebruiken maar niet hoe het werkt.

2.4.4 Het maken en aanroepen van een functie

In het algemeen ziet een functie er zo uit:

function foo(argumentenlijst) {
    /*
     * hier komt jouw code
     */
    return result;
}

Deze functie heeft de naam foo. De argumentenlijst bevat nul of meer variabelen. Je kunt de argumentenlijst leeg laten. De haakjes () moet je wel altijd gebruiken om aan te geven dat het om een functie gaat.

2.4.5 Voorbeeld 1: een eenvoudige functie

De simpelste vorm van een functie is zonder argumenten en zonder returnwaarde:

function zegEensHallo() {
   console.log("Hallo");
}

Je kunt deze functie bijvoorbeeld zo gebruiken:

console.log("Demonstratie van een simpele functie, we roepen de functie een paar keer aan");
zegEensHallo();
zegEensHallo();
zegEensHallo();

Let erop dat de naam die je gebruikt om de functie aan te roepen exact hetzelfde is als de naam van de functie. Gebruik daarom bij voorkeur "camel case": vanaf het tweede deel van een woord beginnen met een hoofdletter en verder kleine letters gebruiken.

2.4.6 Voorbeeld 2: teken een rechthoek

In p5.js kennen we de functie rect() om een rechthoek te tekenen. Maar als wel die nou eens zelf willen maken dan kan dat als volgt:

function rechthoek() {
  line(1,1,50,1);
  line(50,1,50,50);
  line(50,50,1,50);
  line(1,50,1,1);
}
 
function draw() {
  rechthoek();
}

Dit was nogal beperkt want het kan alleen maar een rechthoek op een bepaalde positie en met een bepaalde grootte tekenen. En het tekent eigenlijk alleen vierkanten.

Het volgende voorbeeld maakt het al iets flexibeler:

function rechthoek(x,y) {
  line(x,y,x+50,y);
  line(x+50,y,x+50,y+50);
  line(x+50,y+50,x,y+50);
  line(x,y+50,x,y);
}
 
function draw() {
  rechthoek(0,0);
  rechthoek(10,10);
  rechthoek(30,30);
}

Maak zelf eens een functie die niet alleen vierkanten tekent maar ook niet-vierkante rechthoeken.

2.4.7 Voorbeeld 3: Uitrekenen van een afstand

Om de afstand tussen twee punten uit te rekenen moeten we de wortel nemen uit de som van de kwadraten van de verschillen tussen de coordinaten. Dat is een hele mond vol. Een voorbeeldje maakt het hopelijk wat duidelijker:

function distance(x1,y1,x2,y2) {
  return Math.sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) );
}
 
 
if(distance(10,10,20,20) < 10) {
  console.log("Deze punten liggen minder dan 10 pixels uit elkaar");
}
else {
  console.log("Deze punten liggen meer dan 10 pixels uit elkaar");
}

2.4.8 Voorbeeld 4: verder werken met het resultaat van een functie

Uitrekenen van de oppervlakte van een rechthoek. Zoals je weet kun je de oppervlakte berekenen door de breedte en hoogte met elkaar te vermenigvuldigen. Dat gaan we nu eens met een functie doen. Eerst zie je de functie calcSurface(), daarna een paar verschillende manieren om de functie te gebruiken.

De functie calcSurface() heeft twee argumenten en een return-waarde. Met een argument wordt hier bedoeld een variabele die de functie in gaat. Er wordt ook wel gezegd dat je die variabelen "aan de functie meegeeft". In deze functie zijn dat de breedte en hoogte (Eng: width en height).

De return-waarde is een variabele die uit de functie komt. Er wordt ook wel gezegd dat de functie die waarde teruggeeft.

// Hier wordt de functie calcSurface() gemaakt
  function calcSurface(w,h) {
    let surface = w * h;
    return surface;
  }
 
  /*
   * Hieronder wordt de functie calcSurface() op
   * een aantal verschillende manieren gebruikt
   */
 
  // Aanroep met getallen
  console.log("Breedte: 4");
  console.log("Hoogte: 5");
 
  let surface = calcSurface(4,5);
  console.log("Oppervlakte: " + surface);
 
 
  // Aanroep met variabelen
  let breedte=3;
  let hoogte=5;
 
  console.log("Breedte: " + breedte);
  console.log("Hoogte: " + hoogte);
  console.log("Oppervlakte: " + calcSurface(breedte,hoogte) );
 
 
  // Aanroep vanuit een loopje
  for(w=1; w <= 4; w++) {
    for(h=1; h <= 4; h++) {
      console.log("Breedte: " + w);
      console.log("Hoogte: " + h);
      console.log("Oppervlakte: " + calcSurface(w,h) );
    }
  }

Stel dat je de oppervlakten van twee rechthoeken bij elkaar wilt optellen en alleen het totaal laten zien, dan doe je dat zo:

let totalSurface = calcSurface(4,5) + calcSurface(7,7);
  console.log("Totale oppervlakte: " + totalSurface);

2.5 Javascript classes & objecten

Binnen programmeertalen zijn objecten taalconstructies waarmee je groepen van verschillende elementen kunt maken. Dat doe je dan niet achteraf door bestaande dingen in een groep te stoppen, maar vantevoren door een klasse te beschrijven en vervolgens dingen te maken die de eigenschappen van die klasse hebben. Die dingen noemen we objecten. Binnen javascript hebben objeten ook nog een andere betekenis. Als we het in dit hoofdstuk hebben over objecten bedoelen we de hierboven beschreven vorm van objecten.

2.5.1 Voorbeeld

Als we in p5.js een blokje op het scherm willen teken dan kunnen we de functie rect() aanroepen om een rechthoek te tekenen. We moeten dan elke keer bedenken waar de rechthoek moet staan (x,y) en hoe groot hij is (b,h). Verder moeten we aangeven in welke kleur hij getekend moet worden en als we hem willen verplaatsen of laten knipperen moeten we daar ook code voor schrijven.

Dat is voor één blokje wel te doen, maar als je meerdere blokjes wilt tekenen die dan ook nog eens onderling elkaar beinvloeden dan wordt de code al gauw onoverzichtelijke.

Dat kan ook anders: we beschrijven eerst het algemene gedrag van een blokje in een object en daarna kunnen we zo veel van die blokjes maken als we nodig hebben.

2.5.2 Ontwerp: een object beschrijven

Vraag: welke eigenschappen heeft ons blokje en welke functionaliteiten? Antwoord: positie, afmetingen, kleur en vorm. Verder: het blokje moet zichzelf kunnen tekenen.

class Block {
  constructor () {
    this.x = random(500);
    this.y = random(400);
    this.w = 5+random(100);
    this.h = 5+random(100);
    this.blockColor = color(random(255),random(255),random(255));
  }
    draw() {
    fill(this.blockColor);
    rect(this.x, this.y, this.w, this.h);
  }
} // Block{}

2.5.3 Realisatie: een object maken

let myBlock;
 
function setup() {
  createCanvas(640,480);
  myBlock = new Block();
} // setup()
 
 
function draw() {
  background(200);
  myBlock.draw();
} // draw()

Het complete werkende voorbeeld vind je hier:

2.5.3.1 Meerdere objecten maken

let myBlock1;
let myBlock2;
let myBlock3;
 
function setup() {
  size(640,480);
  myBlock1 = new Block();
  myBlock2 = new Block();
  myBlock3 = new Block();
}
 
function draw() {
  background(200);
  myBlock1.draw();
  myBlock2.draw();
  myBlock3.draw();
}

2.6 Arrays

Een array is een variabele die een rij waarden kan bevatten, een soort genummerde lijst. Je kunt het zien als een straat met huisnummers. De naam van de straat is de naam van het array. Het huisnummer bepaalt het vakje van het array.

Voorbeeld:

let LeidseStraat  = [];

Dit maakt een nieuwe array. Hierbij geef je met de index (bv. 0, 1, 2, 3) aan naar welk element in het array je wijst. In Javascript krijgt het eerste element als index 0.

In het volgende stukje code krijgt elk element een waarde:

LeidseStraat[0] = "Familie Jansen";
LeidseStraat[1] = "Bakker Bard, de muzikale bakker";
LeidseStraat[2] = "Familie De Bruin";
LeidseStraat[3] = "Kees van den Berg";

Om de inhoud van een element op te vragen gebruik je ook de haakjes [ ] met daartussen het nummer (de index) van het element:

let huisnummer=2;
let bewoner = LeidseStraat[huisnummer];
console.log("Op nummer " + huisnummer + " woont " + bewoner);

Met length kun je opvragen uit hoeveel elementen een array bestaat, bijvoorbeeld zo:

let aantalWoningen = LeidseStraat.length;

Dit geeft aan hoeveel plaatsen deze array beschikbaar heeft. Het wil echter niet zeggen dat alle vakjes gevuld zijn. Er kunnen ook plaatsen zijn die null bevatten, wat betekent dat het geen inhoud heeft.

Met een loopje kun je alle waarden laten zien:

for(let index=0; index < LeidseStraat.length; index++) {
  console.log(LeidseStraat[index]);
}

Nog een voorbeeld:

let trackDuration  = [];
 
trackDuration[0] = 24.5;
trackDuration[1] = 247.9;
trackDuration[2] = 200.0;
trackDuration[3] = 86.3;
 
for(let index=0; index < trackDuration.length; index++) {
  console.log(trackDuration[index]);
}

2.6.1 Initialiseren

Je kunt een array direkt bij het maken al initialiseren, dat betekent voorzien van een aantal waarden. Bij een array met namen van steden gaat dat als volgt:

let stad = ["Groningen","Utrecht","Amsterdam"];
 
for(let index=0; index < stad.length; index++)
{
  console.log("Stad " + index + " is " + stad[index]);
}

2.7 Array van objecten

Nu je weet hoe je een object maakt en hoe je een lijst van dingen maakt, zoals hierboven met een array, kun je die twee combineren tot een lijst van objecten. Breid het voorbeeld zelf uit met het gedrag van de boxes zoals beweging, stuiteren, verandering van kleur etc.

let boxArray = [];
let aantalBoxes=100;
 
function setup() {
  canvas = createCanvas(500,500);
  canvas.position(250,100);
 
  for(let i=0; i<aantalBoxes; i++){
    boxArray[i] = new Box();
  }
 
} // setup
 
function draw() {
  for(let i=0; i<aantalBoxes; i++){
       boxArray[i].teken();
  }
} // draw
 
class Box {
  // positie, breedte, hoogte en kleur
  constructor() {
    this.x = random(50,400);
    this.y = random(50,400);
    this.w = random(10,40);
    this.h = random(10,40);
    this.kleur = color(random(200),0,0);
  }
  teken() {
    fill(this.kleur);
    rect(this.x,this.y,this.w,this.h);
  }
}
 
 //verplaatsen
 //botsen
}