Klasser og objekter i Javascript
Objekter
Vi kan lage et enkelt objekt, ikke en klasse, på følgende enkle måte:
var ole={ navn:"ole olsen", adresse:"moss", }
og
var T=ole.navn+' fra '+ole.adresse;
vil gi T : ole olsen fra moss
Vi kan også gjøre slik:
var per={ navn:"per pettersen", adresse:"sarp", showMe:function(){return this.navn+' fra '+this.adresse;} }
og
var T=per.showMe();
vil gi T : per pettersen fra sarp
Hvis vi ser nærmere på formen vi har gitt til objektene,
{ name:value, name:value, }
så er det den samme formen som vi har som utgangspunkt når vis skal
produsere JSON-tekst, se modulen
JSON
.
Det betyr, uten at det er viktig i denne sammenhengen, at dersom vi har
et objekt uten funksjoner, skal vi kunne klone objektet ole slik:
var jens=JSON.parse(JSON.stringify(ole));
Antagelig ikke særlig effektivt
Grunnformen er også den samme formen vi bruker når vi skal sende et objekt, eller om vi vil: et sett med verdier som parameter til en funksjon. F.eks. vil vi initiere et AJAX-kall i JQuery, se modulen jQueryAjax , med noe slikt:
$.ajax( { url:address, async=False, success:function(data) { // do something }, error:function(data) { // do something else }, } );
Antall "name:value"-par vil variere. Det er lett for mottageren å sjekke om en egenskap er satt ved ganske enkelt å spørre:
if (ole.showMe) alert(ole.showme())
Klasser
Vi så ovenfor på objekter. Nå skal vi se hvordan vi kan introdusere klasser ved hjelp av en konstruktor, som har formen av en funksjon.
Vi begynner med et enkelt eksempel.
Anta:
function person() { var navn='ole'; var adresse='halden'; }
Hvis vi nå skriver følgende:
var naboen = new person();
har vi satt opp et nytt object, naboen. Merk at vi altså oppretter objektet ved å kalle en funksjon. Funksjonskallet fungerer både som konstruktør og objektbeskrivelse, og det returnerer et objekt. De to egenskapene, navn og adresse, er lokale og vi har ikke stor glede av naboen. Dersom vi forsøker å få tak i naboen.navn, får vi beskjed om at dette er undefined. En var deklarasjon avgrenser variabelene til den lokale blokken, i dette tilfellet funksjonen eller, om vi vil, objektet.
Vi skriver om funksjonen person med tanke på at vi vil ha tilgang til egenskapene.
function person() { this.navn='ole'; this.adresse='halden'; }
dersom vi nå oppretter et objekt:
var naboen = new person();
kan vi skrive naboen.navn og få ole.
Vi kan videre lage følgende:
function person(nvn,adr) { this.navn=navn; this.adresse=adr; }
dersom vi nå oppretter et objekt:
var naboen = new person('Hans','Moss');
og naboen.adresse gir oss Moss.
Vi tar eksempelet et steg videre og forsøker å lage en konstruksjon med lokale egenskaper og synlige, public, metoder.
function person(nvn,adr) { var navn=nvn; var adresse=adr; this.getNavn = function(){return navn} this.getAdresse = function(){return adresse} }
dersom vi nå oppretter et objekt:
var naboen = new person('Jens','Sarp');
og naboen.getNavn() gir oss Jens.
Dersom vi lager følgende konstruksjon så har vi en ganske vanlig form, slik vi ofte ordner oss i Java, med lokale egenskaper og åpne metoder.
function person(nvn,adr) { var navn=nvn; var adresse=adr; this.getNavn = function(){return navn} this.getAdresse = function(){return adresse} this.setNavn = function(nvn){navn=nvn} this.setAdresse = function(adr){adresse=adr} }
Alternativt
Vi kan skrive følgende direkte:
var minbil = new Object(); minbil.merke = 'toyota'; minbil.farge = 'blå';
Det vil si at vi tilordner nye egenskaper til et nytt generelt objekt. Og vi kan fortsette med å plante en funksjon:
minbil.visfram = new function() { var vis=function() { return 'Det er en '+this.farge+' '+this.merke; } return vis; };
Setningen:
minbil.visfram();
Vil gi resultatet: "Det er en blå toyota"
Dette viser et par interessante egenskaper ved Javascript. Den mekanismen som gjør at vi kan "plante" en funksjon kalles "closure". Det innebærer at funksjonen visfram kalles, den eksekveres og den returnerer en referanse til den interne funksjonen vis. Det er denne funksjonen, vis, som kjører når vi senere kaller visfram(). Denne mekanismen er nyttig og brukes utstrakt i ikke-trivielle javascriptbiblioteker. Blandt annet er denne mekanismen involvert i det minibiblioteket som er introdusert for enkel AJAX i modulen: Kodestrategier
Eksempelet ovenfor indikerer at vi skal kunne ta for oss et vilket som helst objekt, f.eks. i et bibliotek, og utvide objektet med nye egenskaper og metoder. Vi kan lager to person-objekter og gjør en av dem til bileier:
function person(nvn,adr) { this.navn=nvn; this.adresse=adr; this.visfram=function(){return this.navn+' fra '+this.adresse;} } var person1=new person('Kristian','Askim'); var person2=new person5('Ole','Mysen'); person2.bilmerke='Ford'; person2.visfram = new function() { var vis=function() { return this.navn+' fra '+this.adresse+' kjører '+this.bilmerke; } return vis; }; alert(person1.visfram()) alert(person2.visfram())
De to alertene gir hhv: "Kristian fra Askim" og "Ole fra Mysen kjører Ford".
Siden tester3.html viser hvordan vi kan plante og bruke en ny egenskap ved bildeobjekter, altså document.images.
Den sentrale koden i eksempelet er:
var imList=null; function setCopyRight() { return function() { return this.src+'<br/>CopyRight: '+this.owner; } } function prepareImages(navn) { imList=document.images; for(var ix=0;ix < imList.length;ix++) { imList[ix].owner=navn; imList[ix].copyRight=new setCopyRight(); } }
Prototype og arv
Alle funksjoner, function-objekter, har en egenskap som heter prototype. Denne kan vi bruke til mye rart.
eksempel1
Vi kan lage følgende enkle konstruksjon:
function land(navn) { this.land=navn; } function sted(hvor) { this.by=hvor; } var byliste=new Array(); sted.prototype=new land('norge'); byliste[0]=new sted('moss'); byliste[1]=new sted('halden'); sted.prototype=new land('sverige'); byliste[2]=new sted('uddevalla'); sted.prototype=new land('danmark'); byliste[3]=new sted('aalborg');
En utskrift av byliste, byliste[ix].by+' i '+byliste[ix].land, vil gi oss
moss i norge halden i norge uddevalla i sverige aalborg i danmark
Greitt nok, men gevisnten er ikke stor. Vi ville oppnådd akkurat det samme med å la landsnavnet være en egenskap ved stedet i en flat klassestruktur.
eksempel 2
La oss se på hvordan vi kan subklasse et land til republikk eller kongedømme:
function land(nvn) { this.navn=nvn; this.show=function(){return this.navn}; } function republikk(nvn) { this.prototype=new land(nvn); this.show=function() { return this.prototype.show()+ ' som er en republikk' }; } function kongerike(nvn) { this.prototype=new land(nvn); this.show=function() { return this.prototype.show()+ ' som er et kongedømme' }; } var landliste=new Array(); landliste[0]=new kongerike('norge'); landliste[1]=new kongerike('danmark'); landliste[2]=new republikk('frankrike'); landliste[3]=new republikk('italia');
Som gir ( landliste[ix].show() ):
norge som er et kongedømme danmark som er et kongedømme frankrike som er en republikk italia som er en republikk
Vi kunne oppnådd det samme ved å skrive:
function land(nvn) { this.navn=nvn; this.show=function(){return this.navn}; } function republikk(nvn) { this.prototype=republikk.prototype; this.navn=nvn; this.show=function() { return prototype.navn+' som er en republikk' }; } function kongerike(nvn) { this.prototype=kongerike.prototype; this.navn=nvn; this.show=function() { return prototype.navn+' som er et kongedømme' }; } var landliste=new Array(); kongerike.prototype=new land(); republikk.prototype=new land(); landliste[0]=new kongerike('norge'); landliste[1]=new kongerike('danmark'); landliste[2]=new republikk('frankrike'); landliste[3]=new republikk('italia');
Vi ser at dette åpner for at vi dynamisk kan endre hva som er prototype for en klasse, altså hvilken klasse vi ønsker å subklasse.
eksempel 3
Vi kan ta for oss personer og bileiere. Vi tenker oss altså at bileier skal være en subklasse av person. Vi kan da lage følgende konstruksjon:
function person(nvn,adr) { this.navn=nvn; this.adresse=adr; this.show=function(){return this.navn+' fra '+this.adresse} } function bileier(mrk,frg,nvn,adr) { this.merke=mrk; this.farge=frg; this.prototype=new person(nvn,adr); this.show=function() { return this.prototype.show()+' kjører en ' +this.farge+' '+this.merke } }
og vi kan bruke det til å lage en array av personer, der noen er bileiere, slik:
var liste=new Array(); liste[1]=new bileier('toyota','blå','ole','moss'); liste[2]=new bileier('ford','gul','hans','mysen'); liste[3]=new bileier('vw','grønn','gudrun','marker'); liste[3].prototype=new person('kristian','sarpsborg'); liste[4]=new bileier('opel','hvit'); liste[4].prototype=new person('marit','askim'); liste[5]=new person('sverre','rakkestad');
En utskrift av lista, liste[i].show(), vil gi følgende:
ole fra moss kjører en blå toyota hans fra mysen kjører en gul ford kristian fra sarpsborg kjører en grønn vw marit fra askim kjører en hvit opel sverre fra rakkestad
Merk liste[3], som "bytter eier", altså får ny person. I andre språk ville vi vel kalle dette dynamisk subklassing.
Merk også liste[4], som etableres uten eier. Vi ser litt nærmere på dette. Hvis vi skriver:
p=new bileier('opel','hvit'); alert(p.show()); p.prototype=new person('marit','askim'); alert(p.show());
vil vi i tur og orden få:
undefined fra undefined kjører en hvit opel marit fra askim kjører en hvit opel
eksempel 4
Endre prototype for en std klasse.
Javascript har ikke noen funksjoner for å trimme text. Det vil si å fjerne whitespace fra begge eller en av endene på en string. Det er ganske nyttig å ha tilgang på denne funksjonaliteten når vi f.eks. skal fjerne tomme linjer i starten eller slutten av en tekst. Følgende løsning basert på regexp finnes i litt forskjellige varianter på nettet:
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g,""); } String.prototype.ltrim = function() { return this.replace(/^\s+/,""); } String.prototype.rtrim = function() { return this.replace(/\s+$/,""); }
Når disse funksjonene er introdusert vil de være virksomme for alle stringer og vi kan skrive:
var T='\t Olsen \n\n\n'; alert('|'+T.trim()+'|');
og få ut:
JSON
En drøfting av praktisk bruk av objektorientert javaskripting må ta med JSON (JavaScript Object Notation) som en viktig komponent. I dette materialet er JSON beskrevet i en egen modul, JSON . Der er fokus satt på JSON som en effektiv måte å overføre og tolke data i forbindelse med AJAX-løsninger.