Functies als waarden

In JavaScript zijn functies gewoon waarden — net als getallen, strings en arrays. Je kunt ze opslaan in een variabele, meegeven als argument en teruggeven vanuit een functie.

// Functie opslaan in een variabele:
const begroet = function(naam) {
  return `Hallo ${naam}!`;
};

// Functie opslaan als property van een object:
const acties = {
  optellen: (a, b) => a + b,
  aftrekken: (a, b) => a - b
};

console.log(acties.optellen(3, 4));  // 7
console.log(acties.aftrekken(10, 3)); // 7
Dit principe — functies als "first-class citizens" — is de basis van callbacks, closures en hogere orde functies.

Callbacks

Een callback is een functie die je meegeeft als argument aan een andere functie. Die andere functie roept jouw callback aan op het juiste moment.

function voerUit(functie) {
  console.log('Voor de callback');
  functie(); // roep de meegegeven functie aan
  console.log('Na de callback');
}

function zegHallo() {
  console.log('Hallo!');
}

voerUit(zegHallo);
// "Voor de callback"
// "Hallo!"
// "Na de callback"

Callback met argument

function verwerkNamen(namen, callback) {
  for (const naam of namen) {
    callback(naam);
  }
}

verwerkNamen(['Anna', 'Bob', 'Carol'], (naam) => {
  console.log(`Hallo ${naam}!`);
});
// "Hallo Anna!"
// "Hallo Bob!"
// "Hallo Carol!"

Callbacks ken je al

// setTimeout — callback na vertraging
setTimeout(() => {
  console.log('1 seconde later');
}, 1000);

// addEventListener — callback bij event
knop.addEventListener('click', () => {
  console.log('geklikt!');
});

// Array methods — callback voor elk element
[1, 2, 3].forEach(n => console.log(n));
[1, 2, 3].filter(n => n > 1);
Je gebruikt callbacks de hele tijd — bij events, timers en array methods. Nu weet je hoe dat mechanisme heet.

Hogere orde functies

Een hogere orde functie is een functie die een andere functie accepteert als argument of teruggeeft als resultaat.

Functie accepteren

function pasToeTweeKeer(functie, waarde) {
  return functie(functie(waarde));
}

const verdubbel = n => n * 2;
console.log(pasToeTweeKeer(verdubbel, 3)); // 12 (3 → 6 → 12)

Functie teruggeven

function maakVermenigvuldiger(factor) {
  return function(getal) {
    return getal * factor;
  };
}

const verdubbel = maakVermenigvuldiger(2);
const verdriedubbel = maakVermenigvuldiger(3);

console.log(verdubbel(5));    // 10
console.log(verdriedubbel(5)); // 15

Eigen hogere orde functie

function filteredLog(array, filterFn) {
  array
    .filter(filterFn)
    .forEach(item => console.log(item));
}

const getallen = [1, 2, 3, 4, 5, 6];

filteredLog(getallen, n => n % 2 === 0); // 2, 4, 6
filteredLog(getallen, n => n > 3);       // 4, 5, 6

Closures

Een closure is een functie die toegang heeft tot variabelen van de buitenliggende scope, ook nadat die buitenliggende functie al is uitgevoerd.

function maakTeller() {
  let teller = 0; // lokale variabele

  return function() {
    teller++;
    return teller;
  };
}

const tel = maakTeller();
console.log(tel()); // 1
console.log(tel()); // 2
console.log(tel()); // 3

// Aparte teller — eigen closure:
const tel2 = maakTeller();
console.log(tel2()); // 1 (begint opnieuw)
Uitleg:
  • maakTeller() wordt uitgevoerd en is klaar
  • Toch onthoudt de teruggegeven functie de variabele teller
  • Elke aanroep van tel() werkt op dezelfde teller
  • tel2 heeft zijn eigen teller — onafhankelijk

Closure met configuratie

function maakGreeter(groet) {
  return function(naam) {
    return `${groet} ${naam}!`;
  };
}

const hallo = maakGreeter('Hallo');
const goedemorgen = maakGreeter('Goedemorgen');

console.log(hallo('Jan'));        // "Hallo Jan!"
console.log(goedemorgen('Lisa')); // "Goedemorgen Lisa!"

Closure voor private staat

function maakBankRekening(beginSaldo) {
  let saldo = beginSaldo; // privé — van buiten niet bereikbaar

  return {
    stortIn(bedrag) {
      saldo += bedrag;
      return saldo;
    },
    neemOp(bedrag) {
      if (bedrag > saldo) return 'Onvoldoende saldo';
      saldo -= bedrag;
      return saldo;
    },
    getSaldo() {
      return saldo;
    }
  };
}

const rekening = maakBankRekening(100);
rekening.stortIn(50);
console.log(rekening.getSaldo()); // 150
console.log(rekening.saldo);      // undefined — privé!
Closures worden veel gebruikt voor private staat, configureerbare functies en het bewaren van context.

Zie closures in actie

Drie counters naast elkaar — elk heeft zijn eigen count-variabele die door de closure wordt "vastgehouden". Klik +1 op counter 1: alleen die telt op. De andere blijven staan.

function makeCounter() { let count = 0; // ← deze variabele wordt "vastgehouden" return () => { count++; return count; }; }

Drie keer aangeroepen → drie onafhankelijke counters:

Counter 1
Closure scope
count = 0
() => { count++; return count; }
Counter 2
Closure scope
count = 0
() => { count++; return count; }
Counter 3
Closure scope
count = 0
() => { count++; return count; }

Vergelijking: wat als count NIET in de functie zat?

Zet count buiten de functie (global) en kijk wat er gebeurt. Klik +1 op een knop hieronder — alle drie tellen op:

let count = 0; // ❌ count staat BUITEN de functie function makeBadCounter() { return () => { count++; // pakt de ENE globale count return count; }; }

Drie "counters" delen nu dezelfde globale count:

Bad Counter 1
geen eigen scope
(deelt globale) count = 0
Bad Counter 2
geen eigen scope
(deelt globale) count = 0
Bad Counter 3
geen eigen scope
(deelt globale) count = 0

Wat zie je? Klik op +1 bij Bad Counter 1 — alle drie de getallen springen tegelijk omhoog. Want er is maar één count variabele. Dat is precies de bug die closures oplossen.

Praktijkvoorbeelden

Debounce — niet te vaak uitvoeren

function debounce(functie, vertraging) {
  let timer;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => functie(...args), vertraging);
  };
}

// Zoekfunctie pas uitvoeren als gebruiker stopt met typen:
const zoek = debounce((zoekterm) => {
  console.log(`Zoeken naar: ${zoekterm}`);
}, 300);

zoekVeld.addEventListener('input', (e) => zoek(e.target.value));

Once — slechts één keer uitvoeren

function eenmalig(functie) {
  let uitgevoerd = false;
  return function(...args) {
    if (!uitgevoerd) {
      uitgevoerd = true;
      return functie(...args);
    }
  };
}

const initialiseer = eenmalig(() => {
  console.log('App geïnitialiseerd!');
});

initialiseer(); // "App geïnitialiseerd!"
initialiseer(); // niets (al uitgevoerd)

Memoize — resultaten onthouden

function memoize(functie) {
  const cache = {};
  return function(arg) {
    if (cache[arg] !== undefined) {
      console.log('Uit cache!');
      return cache[arg];
    }
    const resultaat = functie(arg);
    cache[arg] = resultaat;
    return resultaat;
  };
}

const bereken = memoize((n) => {
  console.log(`Berekening voor ${n}`);
  return n * n;
});

bereken(5); // "Berekening voor 5" → 25
bereken(5); // "Uit cache!" → 25 (geen herberekening)
bereken(6); // "Berekening voor 6" → 36

Samenvatting

Concept Beschrijving
Functie als waarde Functies zijn waarden die je kunt opslaan en doorgeven
Callback Functie die als argument wordt meegegeven
Hogere orde functie Functie die een functie accepteert of teruggeeft
Closure Functie die variabelen van buitenliggende scope onthoudt