Artikel top billede

(Foto: Computerworld)

JavaScript - del 3: Funktionerne

Denne serie i tre dele introducerer dig til de vigtigste elementer i JavaScript. Vi kiggede forrige gang på objekter. Denne gang runder vi af med funktioner, der også virker som objekter.

Af Kenneth Geisshirt, Alt om Data

Denne artikel er oprindeligt bragt på Alt om Data. Computerworld overtog i november 2022 Alt om Data. Du kan læse mere om overtagelsen her.

I forrige afsnit i serien om JavaScript så du, hvordan JavaScript behandler objekter. Der er ingen klasser i JavaScript, men sproget behandler alt som objekter. I stedet for klasser bruger JavaScript en prototype-baseret objekt-model. I et dynamisk sprog som JavaScript, har dette sine fordele. Her i sidste afsnit af vores serie om JavaScript kommer du til at se på funktioner på en hel ny måde. Funktioner er nemlig også objekter, og det betyder, at de kan bruges på samme måde som andre objekter.

Procedural programmering

Har du programmeret i Pascal, C eller PHP tidligere, kender du alt til procedural programmering. At udvikle et program i et proceduralt programmeringssprog handler det meste af tiden om at dele programmet op i procedurer (Pascal) eller funktioner (C). JavaScript har også funktioner som koncept – og du kan godt bruge funktioner på samme måde til at opdele dine programmer i mindre logiske dele.

I infoboks »Proceduralt eksempel« finder du et eksempel på en lille funktion i JavaScript. Funktionen lægger alle tallene sammen i et array. Funktioner i JavaScript kan godt være rekursive, det vil sige; en funktion kan godt kalde sig selv. Eftersom funktionskald ofte implementeres ved hjælp af stakke, er rekursion ikke altid en god idé, selvom en funktion mere elegant kan skrives på den måde. Det er også sandt for JavaScript, men i infoboks »Rekursion« kan du se et eksempel på, at rekursion er muligt.

Metoder

JavaScript er et objekt-orienteret sprog, og det er muligt at lade et attribut i et objekt være en funktion. Den slags attributter omtales i objekt-orienterede programmeringssprog som objektets metoder. Ideen er, at metoderne kun virker på objektets attributter, og du på den måde har mulighed for at skjule data og dets repræsentation. Gennem metoderne nøjes du med at stille en snitflade til rådighed og definerer derved præcist, hvad der kan gøres med data.

I infoboks »Objekter og metoder« finder du et eksempel på et objekt med to metoder (sum og append). Det er vigtigt at forstå et par ting vedrørende eksemplet. For det første sætter JavaScriptvariablen this som en reference til objektet, når en metode kaldes. Det betyder, at du har adgang til objektets attributter og metoder gennem this. I eksemplet findes der en attribut ved navn »data«, som er et array af tal.

For det andet viser eksemplet en anden vigtig egenskab ved funktioner i JavaScript. Metoderne er anonyme funktioner – de har nemlig ikke noget navn. Navnet på metoden er ikke funktionens navn – det navn er attributtens navn. Måske virker det som en ligegyldig detalje, men anonyme funktioner er meget vigtige.

Anonyme funktioner

En del programmeringssprog har – ligesom JavaScript – anonyme funktioner (Perl, Python, Lisp, C# for at nævne et par stykker). Som du allerede har set, bruges anonyme funktioner til at definere metoder i objekter. Men de kan bruges meget mere generelt - for eksempel til call-backs. Call-backs bruges ofte i programmering af brugergrænseflader (hvad skal der ske, når brugeren klikker på knappen). I web-applikationer bruges call-backs især, når brugeren ændrer et valg i en formular eller til behandling af resultatet af et Ajax-kald.

Anonyme funktioner gives ofte som parametre til andre funktioner i JavaScript og på den måde implementeres et call-back. I infoboks »Call-back« kan du se, hvordan det kan gøres. Funktionen sum tager to argumenter. Det første er en funktion, som bruges til at udskrive resultatet af beregningen, men det andet argument er selve data (array af tal). Funktionen sum behøver ikke at tage sig af selve udskrivningen af resultatet, og i stedet for at rette i sum når du vil ændre, hvordan resultatet udskrives, kan du ændre den anonyme funktion i kaldet. På den måde kan du bruge anonyme funktioner til at adskille logiske funktioner (beregning og udskrivning i dette eksempel).

At bruge anonyme funktioner er et særligt paradigme i programmering – det går under betegnelse funktionsprogrammering (functional programmering).

Funktioners metoder

Enhver funktion i JavaScript er et objekt og moder-objektet er Function, det vil sige, alle har en attribut prototype, som peger på Function (som igen peger på Object). Objektet Function definerer metoden apply. og du kan bruge den til at udføre funktionen på noget data.

Første argument til apply er, hvad this skal sættes til, mens andet argument er et array med parametre til funktionen. Har din funktion tre parametre, skal længden af arrayet være tre og så videre.

Tricket med apply er, at det er muligt at bygge argumenter op som et array og så kalde funktionen. Hvis du ikke kender antallet af argumenter på forhånd, kan du bruge apply med en anden smart lille ting i java script: er listen af argumenter tom ved erklæring af funktioner, placeres de i et array ved navn arguments. Vil du vide, hvor mange argumenter, som blev overført til funktionen, kan du tjekke værdien af arguments.length. I infoboksen »Variabel antal argumenter« kan du se, hvordan apply kan bruges sammen med arguments til at bygge en del generisk funktion til at sætte elementer sammen fra et array.

Closures

Det er muligt at have funktioner indeni funktioner i JavaScript – altså indre funktioner som kun er kendt af den omkringliggende funktion. Det er meget praktisk, da det giver dig mulighed for at bryde en funktion ned i mindre logiske blokke.

Virkefelter (scopes) i JavaScript er noget specielle, idet virkefeltet går på en hele funktionen og ikke kun blokken (en blok er alt mellem et par krøllede parenteser). Det har den mærkværdighed at en variable i den ydre funktion kan tilgås af indre funktioner – selvom den først erklæres og bruges længere nede i den ydre funktion. Variable i et virkefelt forsvinder, når en funktion afsluttes – sådan er JavaScript bare ikke helt.

Normalt er et virkefelt på funktionsniveau ikke særlig velset blandt programmører, men i JavaScript har det sine fordele. Eftersom en indre funktion har adgang til hele den ydre funktions variable, betyder det at den ydre funktions data kan fortsætte med at eksistere – selv når ikke eksekveres kode i den længere. Sådan en mekaniske kaldes for closure.

Infoboksen »Unikke id’er« viser et eksempel på, hvordan closures kan bruges til at generere id’er, for eksempel til at give alle html-elementer forskellige id. Den indre funktion har adgang til variablen id og kan tælle den op ved hvert kald, mens variablen uid holder fast i den anonyme, indre funktion. Selv om den ydre funktion ikke længere eksisterer, holder den indre funktion fast i det virkefelt (variablen id), som den ydre funktion erklærer.

Closures bruges i stor udstrækning i programmering af web-applikationer. Ved at binde en anonym funktion med closure til en hændelse (måske klik på en knap eller ændre et valg i en formular), kan håndtering af hændelser skrives meget kompakt og uden brug af globale variabler. Netop ved ikke at bruge globale variabler, er det muligt at skrive asynkrone (og potentielt parallelle) applikationer uden at bekymre sig om låse og semaforer.

Afslutning

Denne serie i tre dele om JavaScript gav dig en kort introduktion til de vigtigste elementer af sproget. Men både sprogets objekter og funktioner er måske ikke helt, som du er vant til, og det kræver lidt eksperimentering, før du mestrer det. Når du begynder at mestre både objekter og funktioner, vil du se et utroligt elegant programmeringssprog folde sig ud - og dine applikationer vil være interessante og spændende at skrive.

function sum(a) {
var i, x;
x = 0;
for (i in a) {
x = x+a[i];
}
return x;
}
console.log(’sum = ’+sum([1,2,6,4]));
Infoboks – Rekursion
function fib(n) {
if (n > 1) {
return fib(n-1)+fib(n-2);
} else {
return 1;
}
}
console.log(‘Fib(5) = ‘+fib(5));

function sum(print, data) {
var i, x;
x = 0;
for (i in data) {
x = x+data[i];
}
print(x);
}

sum(function(y) { console.log(’sum = ’+y); }, [1,2,4,6]);
sum(function(y) { console.log(y+’ = sum’); }, [1,2,4,6]);

function join() {
var i, s;
s = ‘’;
for(i=0; i<arguments.length; i++) {
s = s+’:’+arguments[i];
}
return s+’:’;
}

console.log(join.apply(null, [1,2,3,4]));
console.log(join.apply(null, [‘hello’,’world’]));

var obj = {
data: [],
append: function (x) {
this.data.push(x);
},
sum: function() {
var i, x;
x = 0;
for (i in this.data) {
x = x+this.data[i];
}
return x;
}
}

obj.append(1);
obj.append(2);
obj.append(4);
console.log(’sum = ’+obj.sum());

var uid = (function() {
var id;
id = 0;
return function() {
id++;
return id;
};
})();
console.log(uid());
console.log(uid());

Infoboks - Closures
function udregn(f, a) {
var i, x;
x = 0;
for (i in a) {
x = x+a[i];
}
f(x);
}

[themepacific_accordion]
[themepacific_accordion_section title="Fakta"]

Få mere at vide

[/themepacific_accordion_section]
[/themepacific_accordion]