» direct naar zoek en menu

Tijdschrift voor webwerkers » Artikel #128

Object georiënteerd JavaScript - Eeuwig onderschat

Objecten zijn bij het programmeren heel belangrijk. Technisch gezien kun je zonder, maar praktisch gezien eigenlijk niet meer. In dit artikel leg ik uit dat het gebruik van objecten in JavaScript helemaal niet moeilijk is. Ook zal ik enkele voorbeelden laten zien die ook voor de expert nog interessant kunnen zijn.

JavaScript gaat op een unieke manier om met objecten: een array, associative array en object zijn allemaal hetzelfde. Dat lijkt verwarrend, maar om sommige gevallen kan het zeer handig zijn.

Formele definities

Allereerst is het handig twee dingen uit elkaar te houden:

Een klassiek voorbeeld om dit te illustreren is de lamp: je definieert een class Lamp met twee methods, aan() en uit() en één property, status. Vervolgens kun je verschillende instances van deze class gaan maken:

var lamp1 = new Lamp(), lamp2 = new Lamp();
lamp1.aan();
lamp2.aan();
lamp1.uit();
alert(lamp1.status + " " + lamp2.status);

Nadat alle methods zijn toegepast is lamp1 uitgeschakeld en lamp2 ingeschakeld.

In JavaScript kunnen we verschillende soorten objecten (en classes) onderscheiden:

Objecten maken

We blijven nog maar even bij ons lampje. De class kunnen we als volgt definiëren:

function Lamp(){
 this.status = false;
 this.aan = function(){
  this.status = true;
 }
 this.uit = function(){
  this.status = false;
 }
}

We zien hier iets heel vreemds: het keyword function wordt gebruikt om een class te definiëren. Het keyword class bestond in JavaScript 1.0, maar is daarna niet meer ondersteund. Het is op internet dan ook erg moeilijk (en overbodig) om informatie te vinden over het exacte gebruik daarvan.

Iets anders dat opvalt, is dat een method eigenlijk slechts een property is van het type Function.

Een ander voorbeeld: ik heb in mijn huis één televisie (en dus niet meerdere); die wil ik ook in JavaScript voorstellen. Het is in dit geval overbodig een class te maken, de constructor uit te voeren en het resultaat daarvan op te slaan in een variabele. Ik kan ook een inline object maken:

tv = {
 zenders : new Array(),
 zender_toevoegen : function(naam){
  tv.zenders[tv.zenders.length] = naam;
 }
};

Nu is het eenvoudig om een zender toe te voegen: tv.zender_toevoegen("Nederland 1");

Objecten en (associative) arrays

Objecten, arrays en associative arrays zijn eigenlijk drie dezelfde dingen in JavaScript. Dat maakt de taal ook zo bijzonder. Eigenlijk is een inline object een associative array en een associative array waarvan de indices van het type Number zijn, is een gewone array. (Let op: een gewone array heeft enkele speciale properties, zoals length, en enkele methods, zoals sort().) Dat is leuk, maar is het ook handig?

Jazeker! Als een object ook als een associative array gezien kan worden, betekent dat dit ook kan:

mijnObject["property1"]

Die string kan echter overal vandaan komen. Die kan ook samengesteld zijn, die kan uit een variabele komen, etc. Dat is ook het principe van de for-in loop.

for(t in mijnObject){
 alert("mijnObject[" + t + "]" + "=" mijnObject[t]);
}

Dit is wat JavaScript zo uniek maakt als het gaat om objecten behandelen: je kunt van een object een associative array maken en andersom, net wanneer het je uitkomt en je hoeft er niets voor te doen.

Extending objects

In veel object-oriented talen kennen we een keyword extends. Dat betekent zoveel als: neem het oude object en plak er wat aan vast. JavaScript heeft dat keyword helemaal niet nodig. De noodzaak van dit keyword werd namelijk veroorzaakt door het feit dat je niet zomaar properties toe kunt voegen; in JavaScript kan dat wel:

var myObject = new Object();
myObject.newProperty = "foo";

Dit is geheel valide JavaScript. Het Object object is een native object. Het is een object zonder functionaliteit: het enige wat het doet is 'object zijn'. Je kunt er echter, zoals in het voorbeeld, wel properties en methods aan toevoegen.

Er zijn overigens ook situaties waarin dit heel belangrijk is. Stel, we willen een soort rollover maken: als je met de muis over een div beweegt, moet er elders op de pagina een stukje tekst (de display-tekst) verschijnen. Dat is natuurlijk makkelijk te maken, maar nu willen we ook dat we de display-tekst later nog kunnen wijzigen. Een mogelijke oplossing is dan de volgende:

function rollover(text, statustext){
 var r = document.createElement("div");
 r.txt = statustext;
 r.innerHTML = text;
 
 r.onmouseover = function(){
  document.getElementById("display_div").innerHTML = this.txt;
 }
 
 return r;
}

var mijn_rollover = rollover("Schuif hier met de muis.", "U hebt geschoven.");

window.onload = function() { document.body.appendChild(mijn_rollover); }

...

<div id="display_div"></div>

Doordat we gewoon properties kunnen toevoegen, kunnen we de informatie over het object bundelen met de <div>. Let op: rollover() is nu niet de constructor, maar een gewone functie. De constructor wordt ergens in document.createElement() uitgevoerd. Daar hebben wij geen inzicht in.

Het voordeel van deze constructie is, dat we later in de code deze regel op kunnen nemen om de display-tekst te wijzigen:

mijn_rollover.txt = "Dit is een nieuwe display-tekst.";


Toepassingen

Hierboven heb ik al één toepassing genoemd, maar er zijn nog talloze andere voorbeelden te bedenken. Ik zal er enkele noemen. Sommigen heb ik zelf geïmplementeerd, anderen niet. Of ik nu spreek van een toepassing van een associative array of van een object, is soms niet helemaal duidelijk; dat komt omdat het verschil er in JavaScript eigenlijk niet is.

JSON

Data uitwisselen kan met XML, maar ideaal is dat niet. Je hebt er een parser voor nodig, je hebt te maken met veel entity's, etc. Een JavaScript object kan eenzelfde soort hiërarchische structuur hebben als een XML-document en is daarmee erg handig om informatie in op te slaan.

Een parser is, als het gaat om JavaScript, niet nodig; omdat de JSON-syntax een subset is van de objectnotatie van JavaScript, kan binnenkomende data gemakkeljk geparst worden via de eval()-functie. Als we in een andere taal een bestand willen lezen dat met JSON is opgemaakt, kunnen we eenvoudig een parser schrijven. Op www.json.org zijn er overigens al diversen te vinden voor verschillende talen.

Een adressenbestandje is in JSON zo gemaakt:

{
 Wouter : {
  Straat : "Keizersgracht",
  Huisnummer : 12
},
Joris : {
  Straat : "Zeestraat",
  Huisnummer : 54
},
Koen : {
  Straat : "Bergerweg",
  Huisnummer : 16
}
}

Zie voor meer informatie ook http://en.wikipedia.org/wiki/JSON.

DCO (Drag coordination object)

Als een pagina met dynamic HTML veel drag-and-drop-acties vereist, kan het soms handig zijn één object te maken dat dat regelt. Dit werkt als volgt: als een object verplaatsbaar moet zijn, wordt het aangemeld bij DCO. Bij deze aanmelding zorgt DCO ervoor dat de juiste event handlers worden toegevoegd. Het is belangrijk dat dit centraal geregeld wordt, omdat niet alleen aan het te verplaatsen object een event handler moet worden toegevoegd, maar ook aan de body en het is niet de bedoeling dat elk verplaatsbaar object zijn eigen event handlers aan de body toevoegt; dat wordt een zootje. DCO is een inline object.

Zie voor meer uitleg: DCO: drag coordination object

Namespaces

Objecten kunnen eenvoudig als namespace gebruikt worden. Het voordeel van het gebruik van namespaces is dat functienamen niet kunnen conflicteren. Als veel programmeurs aan eenzelfde project werken, is dat risico namelijk vrij groot. In JavaScript zijn objecten zeer geschikt als namespaces, omdat men properties en methods ‘on the fly’ kan toevoegen.

NaarVoren = {
 TestObject : {
   DoSomething : new function() { alert("foo"); }
 }
};
NaarVoren.TestObject.DoSomething();

Option objects

In mijn scripts komen soms functies voor die zeer veel argumenten vereisen. Lang niet al die argumenten zijn dan echter noodzakelijk. Dit leverde zoveel problemen op met de volgorde, dat ik tegenwoordig één object, options genaamd, aan een functie meegeef, met daarin alle benodigde informatie. Dergelijke objecten maken de code ook een stuk leesbaarder.

function info(options){
 alert("Naam: " + options.naam + "\nTel. nr: " + options.telnr);
}

info({
 naam : "Vincent de Haan",
 telnr : "06-12345678"
});

Een uitgebreid voorbeeld

Op veel sites kun je dingen kopen. We gaan een bestelformulier maken dat zijn informatie opslaat in een array, bestaande uit objecten. Het systeem heeft de volgende eigenschappen:

Omdat alle verschillende soorten producten verschillende eigenschappen hebben, is een tabel (lees: multidimensionale array) geen geschikte oplossing. We gaan een array met objecten maken.




[geen product geselecteerd]

Natuurlijk is deze interface niet af. Het gebruik van prompt() is buitengewoon onhandig en er moet nog een verzendingsmechanisme ontworpen worden. Het voorbeeld demonstreert echter wel een van de mooie dingen van Object Oriented JavaScript. Ieder object in de array correspondeert namelijk met een product in het winkelwagentje. Die array kunnen we eenvoudig in JSON voorstellen en naar een server opsturen.

Verder lezen

Auteur

Vincent de Haan

zat op het moment van publiceren in 6-gymnasium en houdt zich in zijn (rijkelijk aanwezige) vrije tijd bezig met webontwerp. JavaScript is daarbij zijn grote specialiteit en hij houdt van esthetisch programmeren. Dat wil zeggen dat de code mooi geformuleerd moet zijn; als het resultaat er goed uitziet is dat leuk, maar bijzaak.

Tegenwoordig studeert hij wiskunde en natuurkunde aan de universiteit van Amsterdam. Dat weerhoudt hem echter niet van zijn passie voor Javascript.

Publicatiedatum: 29 september 2006

Let op

Naar Voren is op 18 juli 2010 gestopt met publiceren. De artikelen staan als een soort archief online. Het kan dus zijn dat de informatie verouderd is en dat er inmiddels veel betere of makkelijkere manieren zijn om je doel te bereiken.

Copyright © 2002-heden » NAAR VOREN en de auteurs