Objecten

In dit hoofdstuk leer je werken met objecten.


Objecten

Je weet ondertussen hoe je variabelen en arrays gebruikt, en hoe je loops gebruikt om code te herhalen voor elk element in een array.

We gaan nu werken met een nieuw soort datastructuur, een object. Een object is een groep van variabelen en functies die bij elkaar horen. Als we in p5.js meerdere vormen willen animeren of interactief willen maken, dan moeten we de eigenschappen van die vormen opslaan in objecten. Een object kan meerdere variabelen bevatten, net zoals een array, maar alle individuele variabelen krijgen een key, een naam, en een value, een waarde. Dat noemen we key/value paren. Objecten zijn verder net als iedere andere waarde in JavaScript: je kan ze in variabelen opslaan, ze in arrays stoppen, of zelfs objecten in objecten opslaan.

De basis syntax om een object te creëren ziet er zo uit:

let kat = {naam: "Heinz", leeftijd: 14, gewicht: 12.2};

Dit statement creëert een object en koppelt deze aan de variabele kat. Dit object heeft drie keys: naam, leeftijd en gewicht. Je kunt zelf de namen van je variabelen en keys verzinnen, maar zorg er altijd voor dat de namen die je kiest duidelijk maken om welke eigenschappen het gaat. Om nu toegang te krijgen tot een waarde bij een specifieke key, gebruik je de volgende syntax:


console.log(kat.naam);  // Heinz
console.log(kat.leeftijd);  // 14

Dit noemen we dot-notatie. Je kun een nieuw key/value paar toevoegen aan een object door de syntax van hierboven te gebruiken voor het "=" teken en de waarde die je wilt toevoegen aan de rechterkant:

kat.kleur = "#000000";
console.log(kat);  // {naam: "Heinz", leeftijd: 14, gewicht: 12.2, kleur: "#000000"}

Het is ook makkelijk om een array van objecten te maken, dit is de meest gebruikte manier voor het werken met objecten. Je zal het veelvuldig gebruiken in je net begonnen carriere als computerprogrammeur. Meestal zal je arrays inladen van een bron van buiten (zoals een API), of je zal een object laten genereren gedurende je programma (bijvoorbeeld voor het opslaan van data van gebruikersinput). Maar je kan ook direct een eigen object schrijven, zo ziet dat eruit:

let katten = [
  {naam: "Heinz", leeftijd: 14, gewicht: 12.2},
  {naam: "Dolly", leeftijd: 9, gewicht: 8.9},
  {naam: "Vladimir", leeftijd: 8, gewicht: 11.0}
];

Nu is katten een array van objecten. Objecten uit deze array selecteren doe je als volgt:

console.log(katten[0]);  // {naam: "Heinz", leeftijd: 14, gewicht: 12.2}
  console.log(katten[2]);  // {naam: "Vladimir", leeftijd: 8, gewicht: 11.0}

En om de waarde van een key uit een object te krijgen in de lijst schrijf je:

console.log(katten[1].leeftijd);  // 9

Je kun ook de waarde van een key in een object aanpassen. Bijvoorbeeld als er ééntje jarig is geweest. Of als een poes besluit dat zij voortaan eigenlijk liever als kat door het leven gaat:

katten[1].leeftijd += 1;
  katten[1].naam = "Johnny";
  console.log(katten[1]);  // {naam: "Johnny", leeftijd: 10, gewicht: 8.9}

Hier is een voorbeeld van hoe je een loop maakt om door een array van objecten heen te gaan. Deze loop print de som van het gewicht van alle katten uit de array:

let gewichtSom = 0;
for (let i = 0; i < katten.length; i++) {
    gewichtSom += katten[i].gewicht;
}
console.log(gewichtSom);  // 32.1

Meerdere arrays?

Laten we eens kijken hoe we in p5.js een aantal cirkels door het beeld kunnen laten bewegen, alsof ze vallen. Dit kunnen we natuurlijk doen door verschillende arrays aan te maken voor de x en de y positie:

Ok, dit werkt. En we passen de waarden in de yArray aan om de cirkels te laten bewegen. Maar de vraag is of deze manier handig is. Want wat als je bijvoorbeeld iedere cirkel ook een eigen diameter en een eigen rgb kleur wil geven? Dan moet je al 6 verschillende arrays aanmaken, 2 voor de positie, 1 voor de diameter en 3 voor de r, g, b waarden. Je zou dan die 6 arrays ook weer in een array kunnen stoppen, maar hoe weet je dan nog welke waarden waarvoor staan? Dat moet je dan allemaal zelf onthouden, en bovendien wordt je code in razend tempo onoverzichtelijker.

Een array van objecten

We kunnen hiervoor beter een array van objecten gebruiken, want dan kunnen we alle eigenschappen benoemen met key/value paren zoals we gezien hebben met de katten. Hieronder doen we hetzelfde als in bovenstaand voorbeeld, maar nu met objecten:

We hebben hier een array aangemaakt die we in de setup functie vullen met 16 objecten, elk met een eigen willekeurige x- en y-positie. Vervolgens gebruiken we in de draw functie de waarden van de keys van elk object om de cirkels te tekenen. Je ziet hier overigens een ander type for loop dan we tot nu toe gezien hebben, met een iets kortere notatie, maar het komt op hetzelfde neer.

let objects = [{x:1, y:1}, {x:2, y:2}, {x:3, y:3}];
for (let i = 0; i < objects.length; i++) {
  let obj = objects[i];
  console.log(obj);
}
is identiek aan:
let objects = [{x:1, y:1}, {x:2, y:2}, {x:3, y:3}];
for (let obj of objects) {
  console.log(obj);
}

Laten we nu ook eens wat random waarden kiezen voor de diameter en de kleur van elke cirkel:

Objecten doen hun eigen ding

In de draw functie gebruiken we nu de r, g, b keys van elk object om de vulkleur te bepalen, en de x, y en diam keys voor de positie en de diameter van de cirkel. En we passen de y key van elk object aan voor de animatie. Maar het gebruik van objecten biedt nog meer voordelen. Objecten zijn namelijk onafhankelijk van elkaar, en dat maakt het mogelijk om variatie in gedrag aan te brengen in onze code. Je kun de objecten die je maakt individueel controleren. Kijk maar naar het volgende voorbeeld, hier geven we elke cirkel een eigen snelheid:

We kunnen de snelheid van de cirkels ook afhankelijk maken van hun diameter, zodat grotere cirkels altijd sneller bewegen dan kleinere (of andersom):

En we kunnen gebruikers bijvoorbeeld ook nieuwe cirkels laten toevoegen door in het scherm te klikken:

Maar wacht eens even.. zien we nu bijna exact dezelfde code in de setup() functie en de mousePressed() functie? Daar kunnen we dan toch beter een aparte functie met parameters voor schrijven. Dat doen we als volgt:

Hier zien we een aantal zaken die we nog niet eerder gezien hebben, zoals new en this keywords, wat zijn dat? En waarom wordt die FallingCircle() functie met een hoofdletter geschreven?

  • FallingCircle() is een functie die gebruikt wordt om een object aan te maken, en zo'n soort functie begint altijd met een hoofdletter.
  • Om een functie aan te roepen die een object aanmaakt gebruik je het new keyword vóór de function call. We maken immers een nieuw object aan.
  • Het this keyword wordt gebruikt om aan te geven dat díe specifieke eigenschappen (x, y, diam, color) bij dát specifieke object horen. Dit ene FallingCircle() object is eigenaar van de waarden die het binnenkrijgt via de parameters, en zo bezit elk object zijn eigen variabelen.

Deze afspraken zijn allemaal gemaakt om je code overzichtelijk te houden, en om onderscheid te maken tussen gewone functies en functies die objecten creëren. Je verdeelt je algoritme hiermee onder in duidelijke kleine stapjes die in de juiste volgorde worden uitgevoerd. Daarnaast is je code ook gemakkelijk uit te breiden, want objecten kun je altijd hergebruiken.

En het gaat nog verder, want objecten kunnen zelf ook weer functies bevatten die je kun aanroepen, zie hieronder hetzelfde voorbeeld weer een beetje aangepast.

Denken in objecten

Objecten zijn een nieuwe manier om je code te organiseren, maar belangrijker nog, ze zijn een nieuwe manier van denken over je code.

Als je bijvoorbeeld aan een aantal vallende cirkels denkt, zie je ze waarschijnlijk niet als een aantal x-waarden en een aantal y-waarden. Je ziet elke cirkel waarschijnlijk als een samenhangende eenheid, waarbij elke eenheid een x- en een y-waarde heeft. Met objecten kun je code structureren die beter aansluit bij hoe je ideeën in je hersenen structureert.

Deze nieuwe manier van denken kan verwarrend zijn. Het kan frustrerend zijn als je er voor het eerst mee werkt. Maar het wordt natuurlijker naarmate je meer code schrijft en meer voorbeelden ziet die objecten gebruiken.

Deze manier van "denken in objecten" wordt objectgeoriënteerd programmeren genoemd en vormt de kern van veel talen en bibliotheken.