Funksjoner og parametere
JavaScript-funksjoner er svært fleksible i sin håndtering av parametere. I dagens artikkel ser vi nærmere på hvordan parametere håndteres og hva vi kan gjøre med dem.
Formelle parametere
JavaScript-funksjoner kan ta "vanlige" formelle parametere som så:
function minFunk(a, b) {
console.log(a);
console.log(b);
}
minFunk(2, 3);
Eksempelet over vil skrive et 2-tall etterfulgt av et 3-tall i konsollet. Ikke noen overraskelser her.
Variabelt antall parametere
JavaScript-funksjoner kan ta et variabelt antall parametere. Dersom vi er usikre på om en funksjon/metode kommer til å motta alle parameterne vi har listet kan vi gi dem standardverdier:
function minFunk(a, b) {
a = a || 1;
b = b || 1;
console.log(a);
console.log(b);
}
minFunk(2); // Skriver ut 2 og så 1
Her ser vi praktisk bruk av sannhetsuttrykk i JavaScript. Dersom a er udefinert vil det evaluere som en "falsy" verdi, og neste del-uttrykk i OR-uttrykket vårt prøves. Dette returnerer verdien 1, som blir as nye verdi. Vær obs på at dette også vil sette a == 1 dersom første parameter er en annen falsy verdi som for eksempel 0. Dette er kanskje ikke det du ønsker:
minFunk(0); // Skriver ut 1 og så 1
Løsningen i slike tilfeller er den noe mer omstendige varianten der du kun setter standardverdier for udefinerte parametere:
function minFunk(a, b) {
a = typeof a !== "undefined" ? a : 1;
b = typeof b !== "undefined" ? b : 1;
console.log(a);
console.log(b);
}
minFunk(0); // Skriver ut 0 og så 1
Denne løsningen er mer presis og vil kun sette standardverdier for parametere som ikke spesifiseres (eller vi sender en verdi som i utgangspunktet er undefined).
arguments
Når du forventer et variabelt antall parametere til en funksjon trenger du faktisk ikke engang å liste parameterne i definisjonen. Du kan for det meste også kalle enhver JavaScript-funksjon/metode med så mange parametere du bare måtte ønske (men det er ingen garantier for resultatene):
console.log("Hei".indexOf()); // => -1
console.log("Hei".indexOf("")); // => 0
console.log("Hei".indexOf("", "")); // => 0
console.log("Hei".indexOf("", "", "")); // => 0
Legg merke til hvordan en metode som i virkeligheten kun reagerer på en parameter kjører helt fint med både ingen, to og tre parametere. Altså kan en JavaScript-funksjon ta et variabelt antall parametere uansett hvordan vi definerer funksjonen vår.
Når vi ikke definerer parameterne i funksjonsdefinisjonen blir verdiene vi sender inn til funksjonen ikke tilordnet noen lokale variabler. For slike tilfeller byr JavaScript på en annen måte å nå funksjonsparameterne på. Alle parametere som sendes til en funksjon er alltid tilgjengelig gjennom den spesielle variabelen arguments.
arguments ligner litt på en array, men er det ikke. Den har en length-egenskap, og du kan aksessere enkeltparametere med klammer: arguments[0] // Første parameter. Men der stopper likheten med arrayer.
function average() {
var sum = 0;
for (var i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
// Spesialtilfelle for ingen parametere for å unngå divisjon med 0
return arguments.length === 0 ? 0 : sum / arguments.length;
}
console.log(average(1, 2, 3, 4)); // 2.5
Vi kan også kombinere bruk av formelle parametere og arguments-"arrayen":
function check(myVar) {
return myVar === arguments[0];
}
console.log(check(1)); // true
Denne funksjonen er selvfølgelig relativt meningsløs, den returnerer alltid true, men illustrerer sammenhengen mellom parametere. Første parameter er alltid tilgjengelig i arguments[0], uavhengig om den også tilordnes en lokal variabel. Neste parameter finner vi i arguments[1] osv.
Navngitte parametere
Navngitte parametere er nyttig for funksjoner som tar mange parametere fordi det gjør at vi slipper å huske rekkefølgen på dem. Det gjør også koden lettere leselig ved å navngi hva de forskjellige parameterne er for noe. Navngitte parametere kan også være nyttig i situasjoner der du ikke nødvendigvis skal ta mange parametere, men hvor settet av mulige parametere er langt større enn reellt antall parametere fra kall til kall (med andre ord: det er mange udefinerte parametere).
JavaScript har ingen støtte for slike parametere, men språkets fleksible objekter tar oss ganske nære. Vi har tidligere sett hvordan objektliteraler lar oss definere objekter on the fly, og dette kan vi bruke som funksjonsparametere:
function updateStyles(element, styles) {
for (var property in styles) if (styles.hasOwnProperty(property) {
element.style[property] = styles[property];
}
}
updateStyles(document.body, { color: "#000",
background: "#fff",
textTransform: "uppercase" });
Dette er et dårlig eksempel fordi å style elementer på denne måten helst bør gjøres fra CSS med eksempelvis klasser som grensesnittet mellom JavaScript og CSS. Men, det er et godt eksempel fordi settet med mulige parametere er veldig stort, og dette gir oss en fleksibel måte å sende/ta imot disse parameterne på.
Standardverdier
Noen ganger er det også interessant med standardverdier på parametere som kommer via objektliteraler. Dette kan vi få til på følgende måte:
function ajax(options) {
options.onComplete = options.onComplete || function() {};
options.onFailure = options.onFailure || function() {};
options.onSuccess = options.onSuccess || function() {};
// ...
}
Som tidligere: dersom du ønsker å faktisk kunne ta imot falsy verdier må du ta i litt hardere:
options.onFailure = typeof options.onFailure !== "undefined" ? options.onFailure : null;
.
Ikke spesielt elegant. La oss få på plass en støttefunksjon her:
testExtendEmpty: function() { with(this) {
var obj = {};
var obj = extend(obj, { a: 1, b: 2 });
assertEqual(1, obj.a);
assertEqual(2, obj.b);
}},
testExtendPreserveExisting: function() { with(this) {
var obj = { c: 32 };
var obj = extend(obj, { a: 1, b: 2 });
assertEqual(1, obj.a);
assertEqual(2, obj.b);
assertEqual(32, obj.c);
}},
testExtendOverwrite: function() { with(this) {
var obj = { b: 4, c: 32 };
var obj = extend(obj, { a: 1, b: 2 });
assertEqual(1, obj.a);
assertEqual(2, obj.b);
assertEqual(32, obj.c);
}}
Dette er altså en funksjon som kombinerer to objekter ved å skrive egenskaper fra det andre parameteret over i det første. Den kan implementeres som så:
function extend(object, properties) {
for (var prop in properties) if (properties.hasOwnProperty(prop)) {
object[prop] = properties[prop];
}
return object;
}
Og den tilfredsstiller testene. Med denne i boks kan vi forenkle standardverdier for navngitte parametere via objektliteraler:
function ajax(options) {
options = extend({ onComplete: function() {},
onFailure: function() {},
onSuccess: function() {} }, options);
// ...
}
Her skrives de verdiene som faktisk er spesifisert i options over standardverdiene som sendes rett til extend-funksjonen. Resultatet er et objekt som i det minste har alle egenskapene vi forventer - noen med verdier fra brukeren og noen med standardverdier. Dersom brukeren sender flere parametere enn vi har standardverdier for vil disse også være tilgjengelige.
Det var i grunnen det jeg hadde å si om parametere til JavaScript-funksjoner/metoder. Vel møtt for en prat om events i morgen!
Kommentarer
Jenny
8. desember, 15:04
Christian
8. desember, 15:32
Ser forøvrig at testene i dette innlegget med hell kunne ha vært litt mer forklarende.
Jenny
8. desember, 20:57
Christian
8. desember, 21:42