JavaScript voor beginners: Objecten
In de vorige aflevering hebben we het gehad over strings en de daaraan verwante arrays. Strings zijn in JavaScript geen gegevenstype maar een ingebouwd object. In deze aflevering zullen we dieper ingaan op objecten. Daarbij komen de eigenschappen en methoden van de andere ingebouwde objecten, Date en Math, aan de orde. Vervolgens zullen we het hebben over objecten die je zelf definieert, tenslotte kijken we kort naar zogenaamde browser-objecten.Eigenschappen en methoden
Anders dan in veel andere programmeertalen is "string" in JavaScript geen gegevenstype maar een ingebouwd object. Als gevolg daarvan schrijf je in JavaScript bijvoorbeeld MyString.bold() in plaats van bold(MyString). Met andere woorden: je gebruikt de "methode" bold die bij het string-object hoort, en niet een functie die deel uitmaakt van de taal als geheel.Dat lijkt misschien een subtiel onderscheid, maar in feite is het een van de uitgangspunten van het object-georiënteerd programmeren: een object bevat zowel gegevens ("properties", oftewel "eigenschappen") als de functies ("methods" of "methoden") die bewerkingen op die gegevens uitvoeren.
Een methode met een bepaalde naam (bijvoorbeeld: "toon") kan deel uitmaken van verschillende objecten, terwijl de werking van die methode steeds anders is bij die objecten. Zo zou MijnTekst.toon() tot gevolg kunnen hebben dat een stukje tekst, al dan niet met een speciale opmaak, op het scherm wordt getoond, terwijl MijnVideo.toon() een filmpje zou kunnen afspelen.
Wanneer we een eigen object definiëren, moeten we zelf aangeven wat de eigenschappen en methoden van dat object zijn, en wat de werking van de methoden is. In het geval van ingebouwde JavaScript-objecten is dat reeds voor ons gedaan.
Date-object
We gebruiken het ingebouwde Date-object in onze scripts voor het manipuleren van datums en tijden. Dit object heeft geen eigenschappen, maar wel een aantal methoden. (Voor de meeste ingebouwde JavaScript-objecten geldt dat ze weinig of geen eigenschappen hebben, maar wel veel methoden, terwijl dat voor de objecten die we zelf definiëren meestal andersom zal zijn.)Met de set-methoden van het Date-object kunnen we de tijd of de datum instellen. Laten we eerst een object Geboortedag van het type Date aanmaken; dat gaat met: Geboortedag = new Date("May 1, 1900"), dus met een enkele parameter van het type string. We mogen ook schrijven: Geboortedag = new Date(0,4,1), dus met drie numerieke parameters: achtereenvolgens het jaartal, de maand en de dag.
Indien het jaartal niet voluit wordt geschreven, wordt aangenomen dat het om de twintigste eeuw gaat: 0 wordt dus geïnterpreteerd als 1900. Merk op dat de telling van de maanden loopt van 0 (januari) tot 11 (december). Als we bij het aanmaken van een Date-object geen parameters opgeven, krijgt het object als waarde de huidige datum, dus bijvoorbeeld: Vandaag = new Date().
Vervolgens kunnen we het jaartal van ons voorbeeld wijzigen in 1998 met: Geboortedag.setYear(1998). We schrijven dus niet: Geboortedag = Geboortedag.setYear(1998): we kennen namelijk niet het resultaat van een functie toe aan een variabele, maar we manipuleren de eigenschap van een object met behulp van een methode van dat object!
Met de opdracht document.write(Geboortedag) krijgen we nu iets te zien als: "Fri May 1 00:00:00 UTC+0200 1998" (MS Internet Explorer) of "Fri May 01 00:00:00 GMT+0200 (Romance Daylight Time) 1998" (Netscape Communicator). De precieze weergave hangt af van de browser die gebruikt wordt en de tijdzone waarop de PC staat ingesteld.
De uren, minuten en seconden in het voorbeeld zijn nul omdat we daar bij het aanmaken van Geboortedag geen waarde voor hebben opgegeven; willen we een specifieke tijd, dan schrijven we bijvoorbeeld Geboortedag = new Date("May 1, 1998 8:39:15") of Geboortedag = new Date(1998, 4, 1, 8, 39, 15).
Op soortgelijke wijze kunnen we het maandnummer wijzigen met setMonth of het dagnummer met setDate (en niet setDay, zoals je misschien zou verwachten). Ook kunnen we de uren, minuten of seconden wijzigen met respectievelijk setHours, setMinutes, setSeconds. Met setTime tenslotte kunnen we de datum en tijd instellen door middel van een "offset": het aantal milliseconden voor of na 1 januari 1970, 00:00:00 (een negatieve offset geeft een datum ervoor, een positieve offset een datum erna).
Naast de set-methoden kent JavaScript een aantal get-methoden waarmee informatie kan worden opgevraagd: getDate geeft bijvoorbeeld het dagnummer, en getYear het jaartal. Met getTime krijgen we het aantal milliseconden voor of na 1 januari 1970, 00:00:00, en getTimezoneOffset geeft het verschil in minuten tussen de locale tijd, zoals die op de gebruikte computer staat ingesteld, en Greenwich Mean Time (GMT), waarbij rekening wordt gehouden met zomer- en wintertijd. Met getDay, tenslotte, kunnen we opvragen om welke dag van de week het gaat; het resultaat loopt van 0 (zondag) tot 6 (zaterdag).
De methoden toGMTString en toLocaleString gebruiken we om een datum om te zetten naar stringformaat, waarbij respectievelijk de Internet GMT-conventies of de "locale"-instellingen van de gebruikte computer worden aangehouden. Na Geboortedag = new Date("May 1, 1998 8:39:15"), uitgevoerd op een computer die staat ingesteld op de Nederlandse tijdzone, geeft document.write(Geboortedag.toGMTString()) bijvoorbeeld als resultaat: "Fri, 01 May 1998 06:39:15 GMT" –de Nederlandse tijd is GMT +1, en kennelijk was op die datum sprake van zomertijd. Met document.write(Geboortedag.toLocaleString()) is het resultaat iets als "Friday, May 01, 1998 08:39:15" (afhankelijk van de locale-instellingen van de gebruikte computer).
Merk op dat toGMTString onverwachte resultaten kan geven als voor het gebruikte datum-object geen tijd is gespecificeerd: Geboortedag = new Date("May 1, 1998") gevolgd door document.write(Geboortedag.toGMTString()) geeft bijvoorbeeld "Thu, 30 Apr 1998 22:00:00 GMT", omdat middernacht Nederlandse tijd (zomertijd) overeenkomt met tien uur ‘s avonds GMT!
Math-object
Het Math-object kent, anders dan de meeste ingebouwde JavaScript-objecten, wel een aantal eigenschappen, te weten: E, LN2, LN10, LOG2E, LOG10E, PI, SQRT1_2 en SQRT2 (we moeten hoofdletters gebruiken, anders krijgen we "undefined"!). Omdat het hier eigenlijk om constanten gaat (achtereenvolgens de constante van Euler, de natuurlijke logaritme van 2 en van 10, de log 2 en de Briggse logaritme van het getal e, pi, en de vierkantswortel uit een half en uit twee), kunnen we aan deze eigenschappen geen waarde toekennen (ze zijn "read-only").Bovendien schrijven we bijvoorbeeld: document.write(Math.PI) en niet: document.write(X.PI), waarbij X een door ons aangemaakt Math-object zou zijn. Sterker nog: we maken nooit eigen Math-objecten aan –de opdracht X = new Math() geeft een foutmelding!--, maar gebruiken altijd het ingebouwde Math-object. De "resultaten", d.w.z. de waarde van de hiervoor genoemde eigenschappen of de functieresultaten van de nog te noemen methoden, kunnen we toekennen aan "gewone" (numerieke) JavaScript-variabelen, bijvoorbeeld: X = Math.PI.
Het Math-object kent onder andere de methoden sin, asin, cos, acos, tan en atan. Hiermee worden respectievelijk de sinus, arc sinus, cosinus, etc., berekend voor een hoek die is uitgedrukt in radialen. Het resultaat van Math.sin(Math.PI/2) is bijvoorbeeld 1, want de sinus van 1/2 pi is gelijk aan 1.
Daarnaast kent Math enkele methoden voor afronding. Met ceil krijgen we het kleinste gehele getal dat groter is dan (of gelijk aan) het getal dat we onderzoeken: het resultaat van Math.ceil(12.34) is bijvoorbeeld 13, en het resultaat van Math.ceil(-12.34) is –12. De methode floor geeft juist het grootste gehele getal dat kleiner is dan (of gelijk aan) het onderzochte getal: Math.floor(12.34) is 12, en Math.floor(-12.34) is –13. Met round tenslotte wordt op de "gebruikelijke" wijze afgerond naar het dichtstbijzijnde gehele getal: Math.round(12.34) is 12, en Math.round(-12.34) is –12.
Ook zijn er enkele methoden die te maken hebben met machten. Met log wordt de natuurlijke logaritme van een getal berekend (dus met basis e!), terwijl exp juist e tot de macht van dat getal oplevert. De methode pow gebruikt twee argumenten, de basis en de exponent, en geeft als resultaat de basis tot de macht van de exponent; Math.pow(8,2) bijvoorbeeld is 64. Met sqrt wordt de vierkantswortel uit een getal berekend; Math.sqrt(64) is 8.
Tenslotte kent Math de methoden min en max die respectievelijk het kleinste en het grootste van de twee meegegeven argumenten opleveren; de methode abs die de absolute waarde van een getal geeft (d.w.z. dat getal zonder eventueel minteken); en de methode random (zonder argumenten) die een pseudo-random getal tussen 0 en 1 teruggeeft.
Zelfgedefinieerde objecten
We hebben nu gezien wat de ingebouwde objecten van JavaScript zijn en hoe we die moeten gebruiken. We kunnen echter ook zelf onze eigen objecten definiëren. Daartoe gebruiken we een zogenaamde constructor: een functie die aangeeft wat de naam van het objecttype is en wat de eigenschappen (en eventuele methoden) zijn. Een voorbeeld:function auto(mk,tp, bj)
{
this.merk = mk
this.type = tp
this.jaar = bj
} Met new creëren we vervolgens een "instantie" (een concrete invulling) van dit objecttype: KleineAuto = new auto("Fiat", "Panda", 1996). De term "this" in de constructor verwijst naar het huidige object zelf en wordt gebruikt om de eigenschappen te definiëren; in ons geval heeft een object van het type auto dus drie eigenschappen, te weten merk, type en jaar.
Voor het toekennen van een waarde aan de eigenschappen gebruikten we in het voorbeeld twee strings en een getal. We mogen echter ook zelfgedefinieerde objecten aan eigenschappen toekennen als waarde. Stel dat we ook een objecttype persoon kennen met als constructor:
function persoon(nm, gj)
{
this.naam = nm
this.jaar = gj
} en dat we daarmee als volgt een object hebben aangemaakt: Buurman = new persoon("Piet", 1970). We zouden dan aan de constructor van auto een eigenschap eigenaar kunnen toevoegen die gevuld wordt vanuit een extra argument eg (this.eigenaar = eg); de aanroep voor het aanmaken van KleineAuto zou dan luiden: KleineAuto = new auto("Fiat","Panda",1996,Buurman).
Merk op dat er geen aanhalingstekens om Buurman staan, want we geven een object door en geen string. Het geboortejaar van de eigenaar van deze auto krijgen we dan te zien met document.write(KleineAuto.eigenaar.jaar), terwijl document.write(KleineAuto.jaar) het bouwjaar van de auto geeft. Het voorbeeld toont dat een eigenschap met een bepaalde naam ("jaar") voor mag komen in verschillende objecten.
In het voorbeeld hebben we de definitie van auto uitgebreid om de eigenaar van elke auto op te kunnen nemen. We mogen echter aan zelfgedefinieerde objecten altijd extra eigenschappen toekennen nadat we de objecten hebben aangemaakt, bijvoorbeeld met: KleineAuto.kleur = "rood". Het verschil is, dat nu alleen dit ene specifieke object KleineAuto een eigenschap kleur heeft, maar eventuele andere objecten van het type auto niet.
Behalve eigenschappen kunnen we ook methoden definiëren voor onze objecten. Hiertoe schrijven we eerst de functie die aangeeft wat de methode moet doen, bijvoorbeeld:
function toon()
{
document.write("Ik ben een " + this.merk + " uit " + this.jaar)
} en vervolgens voegen we die functie toe aan de constructor met this.toon = toon (zonder haakjes!).
Als we nu schrijven: KleineAuto.toon() (met haakjes), dan krijgen we op het scherm te zien: Ik ben een Fiat uit 1996.
Eigenschappen en arrays
De twee begrippen "eigenschappen" en (associatieve) "arrays" hangen nauw samen in JavaScript. We kunnen eigenschappen namelijk ook benaderen alsof sprake was van een array. In plaats van KleineAuto.merk schrijven we dan: KleineAuto["merk"], met precies hetzelfde resultaat.We spreken over een associatieve array omdat we de elementen van de array niet benaderen met behulp van een numerieke index maar met behulp van een string die met de op te zoeken waarde is "geassocieerd".
Browserobjecten
Om een tekst op het scherm te tonen, gebruiken we nu al een hele tijd de opdracht "document.write". Dit is een voorbeeld van het gebruik van een browserobject. We hebben gezien dat eigenschappen van objecten zelf ook weer objecten met eigenschappen mogen zijn. Het object "document" is daarvan een voorbeeld: het is zelf een eigenschap van "window", het browserobject van het hoogste niveau.Eigenlijk zouden we dus moeten schrijven: "window.document.write" (dat werkt trouwens ook), maar (alleen) in het geval van window mogen we ook de "verkorte" vorm gebruiken. Behalve document heeft window de eigenschappen location, history en Frame.
We kunnen bijvoorbeeld via window.location de URL van het huidige document opvragen, maar we kunnen er ook een waarde aan toekennen en zo de browser naar een nieuwe URL sturen. Het window-object heeft ook een aantal methoden, zoals open en close voor het openen en sluiten van "popup-vensters" en alert voor het tonen van een waarschuwing.
Ook het object "document" heeft verschillende eigenschappen, zoals bgColor en linkColor voor de kleur van de achtergrond en de (nog niet geselecteerde) links, en lastModified voor de datum waarop het document (d.w.z.: het onderliggende HTML-bestand) voor het laatst is gewijzigd. Daarnaast kunnen de objecten layer, link, image, area, anchor, applet, plugin en form eigenschappen zijn van document, afhankelijk van de gebruikte browser en HTML-versie.
Van de genoemde eigenschappen verdient met name form nadere aandacht: in de volgende aflevering zullen we daarom veel dieper op dit onderwerp ingaan.
Conclusie
We hebben het deze keer gehad over objecten. We hebben gezien hoe we ingebouwde objecten van JavaScript gebruiken, en hoe we zelf onze eigen objecttypen kunnen definiëren. In de volgende aflevering komen "forms" aan de orde: we zullen dan zien hoe we JavaScript kunnen gebruiken voor het valideren van de gegevens die een eindgebruiker invult in een HTML-formulier.
