Maak het JavaScript-interview af: Wat is een afsluiting?

“Master the JavaScript Interview” is een serie berichten die zijn bedoeld om kandidaten voor te bereiden op veelvoorkomende vragen die ze waarschijnlijk tegenkomen als ze solliciteren naar een JavaScript-functie op midden- tot hoger niveau. Dit zijn vragen die ik vaak gebruik in echte sollicitatiegesprekken.

Ik begin de serie met de $40k-vraag. Als je deze vraag fout beantwoordt, is de kans groot dat je niet wordt aangenomen. Als je wel wordt aangenomen, is de kans groot dat je wordt aangenomen als junior developer, ongeacht hoe lang je al als softwareontwikkelaar werkt. Gemiddeld krijgen junior ontwikkelaars 40.000 dollar per jaar minder betaald dan meer ervaren software-engineers.

Closures zijn belangrijk omdat ze bepalen wat wel en niet in scope is in een bepaalde functie, samen met welke variabelen worden gedeeld tussen zusterfuncties in dezelfde containing scope. Begrijpen hoe variabelen en functies zich tot elkaar verhouden is cruciaal om te begrijpen wat er in je code gebeurt, zowel in functionele als objectgeoriënteerde programmeerstijlen.

De reden dat het missen van deze vraag zo nadelig is in een sollicitatiegesprek is dat misverstanden over hoe closures werken een vrij duidelijke rode vlag zijn die een gebrek aan diepgaande ervaring kan verraden, niet alleen in JavaScript, maar in elke taal die veel gebruik maakt van closures (Haskell, C#, Python, etc…).

Coderen in JavaScript zonder kennis van closures is net zoiets als Engels proberen te spreken zonder kennis van grammaticaregels – je kunt je ideeën misschien wel overbrengen, maar waarschijnlijk op een wat onhandige manier.

Je bent ook kwetsbaar voor misverstanden als je probeert te begrijpen wat iemand anders heeft geschreven.

Je moet niet alleen weten wat een closure is, je moet ook weten waarom het belangrijk is, en je moet in staat zijn om gemakkelijk verschillende mogelijke use-cases voor closures te beantwoorden.

Closures worden vaak gebruikt in JavaScript voor object data privacy, in event handlers en callback functies, en in partiële applicaties, currying, en andere functionele programmeerpatronen.

Als je deze vraag niet kunt beantwoorden, kan je dat je baan kosten, of ~$40k/jaar.

Bereid je voor op een snelle follow-up: “Kun je twee veelvoorkomende toepassingen van closures noemen?”

Wat is een closure?

Een closure is de combinatie van een functie gebundeld (ingesloten) met verwijzingen naar de omringende toestand (de lexicale omgeving). Met andere woorden, een closure geeft je toegang tot de scope van een buitenste functie vanuit een binnenste functie. In JavaScript worden closures gemaakt elke keer dat een functie wordt gemaakt, bij het maken van een functie.

Om een closure te gebruiken, definieer je een functie binnen een andere functie en exposeer je deze.

De binnenste functie heeft toegang tot de variabelen in het bereik van de buitenste functie, zelfs nadat de buitenste functie is teruggekeerd.

Toepassing van closures (Voorbeelden)

Ook closures worden vaak gebruikt om objecten data privacy te geven. Data privacy is een essentiële eigenschap die ons helpt te programmeren naar een interface, niet naar een implementatie. Dit is een belangrijk concept dat ons helpt robuustere software te bouwen, omdat implementatiedetails eerder op een afbrekende manier kunnen veranderen dan interfacecontracten.

“Programmeer naar een interface, niet naar een implementatie.”
Design Patterns: Elements of Reusable Object Oriented Software

In JavaScript zijn closures het belangrijkste mechanisme dat wordt gebruikt om gegevensprivacy mogelijk te maken. Wanneer je closures gebruikt voor data privacy, zijn de ingesloten variabelen alleen binnen het bereik van de bevattende (buitenste) functie. Je kunt niet bij de gegevens van een buiten bereik komen, behalve via de geprivilegieerde methoden van het object. In JavaScript is elke methode die binnen het bereik van de sluiting is gedefinieerd, geprivilegieerd. Bijvoorbeeld:

Play with this in JSBin. (Zie je geen uitvoer? Kopieer en plak deze HTML in het HTML-venster.)

In het bovenstaande voorbeeld is de `.get()` methode gedefinieerd binnen de scope van `getSecret()`, waardoor het toegang heeft tot alle variabelen van `getSecret()`, en het een geprivilegieerde methode is. In dit geval, de parameter, `secret`.

Objects zijn niet de enige manier om data privacy te produceren. Closures kunnen ook worden gebruikt om stateful functies te maken waarvan de terugkeerwaarden kunnen worden beinvloed door hun interne toestand, bijvoorbeeld:

const secret = msg => () => msg;

Beschikbaar op JSBin. (Ziet u geen uitvoer? Kopieer en plak deze HTML in het HTML-deelvenster.)

In functioneel programmeren worden closures vaak gebruikt voor gedeeltelijke toepassing & currying. Hiervoor zijn enkele definities nodig:

Application: Het proces van het toepassen van een functie op zijn argumenten om een terugkeerwaarde te produceren.

Partiële toepassing: Het proces van het toepassen van een functie op sommige van zijn argumenten. De gedeeltelijk toegepaste functie wordt teruggegeven voor later gebruik. Gedeeltelijke toepassing fixeert (past gedeeltelijk de functie toe op) een of meer argumenten in de geretourneerde functie, en de geretourneerde functie neemt de resterende parameters als argumenten om de toepassing van de functie te voltooien.

Deeltelijke toepassing maakt gebruik van closure scope om parameters te fixeren. Je kunt een generieke functie schrijven die argumenten gedeeltelijk toepast op de doel functie. Het zal de volgende signatuur hebben:

partialApply(targetFunction: Function, ...fixedArgs: Any) =>
functionWithFewerParams(...remainingArgs: Any)

Als je hulp nodig hebt bij het lezen van de bovenstaande signatuur, kijk dan eens naar Rtype: Reading Function Signatures.

Het neemt een functie die een willekeurig aantal argumenten neemt, gevolgd door argumenten die we gedeeltelijk op de functie willen toepassen, en retourneert een functie die de resterende argumenten neemt.

Een voorbeeld zal helpen. Stel dat je een functie hebt die twee getallen optelt:

const add = (a, b) => a + b;

Nu wil je een functie die 10 bij een willekeurig getal optelt. We noemen het `add10()`. Het resultaat van `add10(5)` moet `15` zijn. Onze `partialApply()` functie kan dat voor elkaar krijgen:

const add10 = partialApply(add, 10);
add10(5);

In dit voorbeeld wordt het argument, `10` een vaste parameter die onthouden wordt binnen de `add10()` closure scope.

Laten we eens kijken naar een mogelijke `partialApply()` implementatie:

Beschikbaar op JSBin. (Zie je geen uitvoer? Kopieer en plak deze HTML in het HTML-venster.)

Zoals je kunt zien, retourneert het eenvoudig een functie die toegang houdt tot de `fixedArgs` argumenten die zijn doorgegeven aan de `partialApply()` functie.

Jouw beurt

Deze post heeft een begeleidende videopost en oefenopdrachten voor leden van EricElliottJS.com. Als je al lid bent, meld je dan aan en oefen nu.

Als je nog geen lid bent, meld je dan vandaag nog aan.

Verken de serie

  • Wat is een Closure?
  • Wat is het verschil tussen Class en Prototypal Inheritance?
  • Wat is een Pure Function?
  • Wat is Functiecompositie?
  • Wat is Functioneel Programmeren?
  • Wat is een Belofte?
  • Soft Skills

Updates:
juli 2019 – Verduidelijkte intro om uit te leggen waarom het fout beantwoorden van deze vraag je een baan of veel geld aan salaris kan kosten.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *