Lukninger i JavaScript forklaret med eksempler

Hvad er lukninger?

En lukning er kombinationen af ​​en funktion og det leksikale miljø (omfang), inden for hvilken denne funktion blev erklæret. Lukninger er en grundlæggende og stærk egenskab ved Javascript. Denne artikel diskuterer 'hvordan' og 'hvorfor' om lukninger:

Eksempel

//we have an outer function named walk and an inner function named fly function walk (){ var dist = '1780 feet'; function fly(){ console.log('At '+dist); } return fly; } var flyFunc = walk(); //calling walk returns the fly function which is being assigned to flyFunc //you would expect that once the walk function above is run //you would think that JavaScript has gotten rid of the 'dist' var flyFunc(); //Logs out 'At 1780 feet' //but you still can use the function as above //this is the power of closures

Et andet eksempel

function by(propName) { return function(a, b) { return a[propName] - b[propName]; } } const person1 = {name: 'joe', height: 72}; const person2 = {name: 'rob', height: 70}; const person3 = {name: 'nicholas', height: 66}; const arr_ = [person1, person2, person3]; const arr_sorted = arr_.sort(by('height')); // [ { name: 'nicholas', height: 66 }, { name: 'rob', height: 70 },{ name: 'joe', height: 72 } ]

Lukningen 'husker' det miljø, hvor den blev oprettet. Dette miljø består af eventuelle lokale variabler, der var inden for omfanget på det tidspunkt, hvor lukningen blev oprettet.

function outside(num) { var rememberedVar = num; // In this example, rememberedVar is the lexical environment that the closure 'remembers' return function inside() { // This is the function which the closure 'remembers' console.log(rememberedVar) } } var remember1 = outside(7); // remember1 is now a closure which contains rememberedVar = 7 in its lexical environment, and //the function 'inside' var remember2 = outside(9); // remember2 is now a closure which contains rememberedVar = 9 in its lexical environment, and //the function 'inside' remember1(); // This now executes the function 'inside' which console.logs(rememberedVar) => 7 remember2(); // This now executes the function 'inside' which console.logs(rememberedVar) => 9 

Lukninger er nyttige, fordi de lader dig 'huske' data og derefter lade dig betjene disse data gennem returnerede funktioner. Dette giver javascript mulighed for at efterligne private metoder, der findes i andre programmeringssprog. Private metoder er nyttige til at begrænse adgangen til kode samt styre dit globale navneområde.

Private variabler og metoder

Lukninger kan også bruges til at indkapsle private data / metoder. Se på dette eksempel:

const bankAccount = (initialBalance) => { const balance = initialBalance; return { getBalance: function() { return balance; }, deposit: function(amount) { balance += amount; return balance; }, }; }; const account = bankAccount(100); account.getBalance(); // 100 account.deposit(10); // 110

I dette eksempel kan vi ikke få adgang balancefra hvor som helst uden for bankAccountfunktionen, hvilket betyder, at vi lige har oprettet en privat variabel. Hvor er lukningen? Tænk på, hvad bankAccount()der vender tilbage. Det returnerer faktisk et objekt med en masse funktioner inde i det, og alligevel, når vi kalder account.getBalance(), er funktionen i stand til at "huske" sin oprindelige reference til balance. Det er kraften i lukningen, hvor en funktion "husker" sit leksikale omfang (kompiler tidsrum), selv når funktionen udføres uden for det leksikale omfang.

Efterligner block-scoped variabler.

Javascript havde ikke et koncept for blok-scoped variabler. Det betyder, at når der for eksempel defineres en variabel inde i en forloop, er denne variabel også synlig uden for forloop. Så hvordan kan lukninger hjælpe os med at løse dette problem? Lad os se.

 var funcs = []; for(var i = 0; i < 3; i++){ funcs[i] = function(){ console.log('My value is ' + i); //creating three different functions with different param values. } } for(var j = 0; j < 3; j++){ funcs[j](); // My value is 3 // My value is 3 // My value is 3 }

Da variablen i ikke har blok-omfang, er dens værdi inden for alle tre funktioner opdateret med loop-tælleren og oprettet ondsindede værdier. Lukning kan hjælpe os med at løse dette problem ved at oprette et øjebliksbillede af det miljø, funktionen var i, da den blev oprettet og bevare dens tilstand.

 var funcs = []; var createFunction = function(val){ return function() {console.log("My value: " + val);}; } for (var i = 0; i < 3; i++) { funcs[i] = createFunction(i); } for (var j = 0; j < 3; j++) { funcs[j](); // My value is 0 // My value is 1 // My value is 2 }

De sene versioner af javascript es6 + har et nyt nøgleord kaldet let, som kan bruges til at give variablen et blockscope. Der er også mange funktioner (forEach) og hele biblioteker (lodash.js), der er dedikeret til at løse sådanne problemer som dem, der er forklaret ovenfor. De kan helt sikkert øge din produktivitet, men det er stadig yderst vigtigt at have kendskab til alle disse problemer, når man forsøger at skabe noget stort.

Lukninger har mange specielle applikationer, der er nyttige, når du opretter store javascript-programmer.

  1. Efterligning af private variabler eller indkapsling
  2. Foretagelse af asynkrone serveropkald
  3. Oprettelse af en blok-scoped variabel.

Efterligning af private variabler.

I modsætning til mange andre sprog har Javascript ikke en mekanisme, der giver dig mulighed for at oprette indkapslede instansvariabler inden for et objekt. At have offentlige instansvariabler kan forårsage mange problemer, når man bygger mellemstore til store programmer. Men med lukninger kan dette problem afhjælpes.

Ligesom i det foregående eksempel kan du oprette funktioner, der returnerer objektlitteraler med metoder, der har adgang til objektets lokale variabler uden at udsætte dem. Således gør dem effektivt private.

Lukninger kan også hjælpe dig med at administrere dit globale navneområde for at undgå kollisioner med globalt delte data. Normalt deles alle globale variabler mellem alle scripts i dit projekt, hvilket helt sikkert vil give dig en masse problemer, når du bygger mellemstore til store programmer. Derfor bruger biblioteks- og modulforfattere lukninger til at skjule et helt moduls metoder og data. Dette kaldes modulmønsteret, det bruger et straks påkaldt funktionsudtryk, der kun eksporterer visse funktioner til omverdenen, hvilket reducerer mængden af ​​globale referencer markant.

Her er en kort prøve af et modulskelet.

var myModule = (function() = { let privateVariable = 'I am a private variable'; let method1 = function(){ console.log('I am method 1'); }; let method2 = function(){ console.log('I am method 2, ', privateVariable); }; return { method1: method1, method2: method2 } }()); myModule.method1(); // I am method 1 myModule.method2(); // I am method 2, I am a private variable

Lukninger er nyttige til at registrere nye forekomster af private variabler indeholdt i det 'huskede' miljø, og disse variabler kan kun fås via den returnerede funktion eller metoder.

Vektorer

En vektor er måske den mest enkle type samling i Clojure. Du kan tænke på det som en matrix i Javascript. Lad os definere en simpel vektor:

(def a-vector [1 2 3 4 5]) ;; Alternatively, use the vector function: (def another-vector (vector 1 2 3 4 5)) ;; You can use commas to separate items, since Clojure treats them as whitespace. (def comma-vector [1, 2, 3, 4, 5])

Du vil se, at det bruger firkantede parenteser, ligesom et array i JS. Da Clojure, ligesom JS, er dynamisk skrevet, kan vektorer indeholde elementer af enhver type, inklusive andre vektorer.

(def mixed-type-vector [1 "foo" :bar ["spam" 22] #"^baz$"])

Tilføjelse af elementer til en vektor

Du kan føje elementer til en vektor ved hjælp af conj. Du kan også forberede dig på en liste ved hjælp af into, men bemærk, at den intoer beregnet til at flette to vektorer, så begge argumenter skal være vektorer, og brugen intoer langsommere end at bruge conj.

(time (conj [1 2] 3)) ; => "Elapsed time: 0.032206 msecs" ; [1 2 3] (time (into [1] [2 3])) ; => "Elapsed time: 0.078499 msecs" ; [1 2 3]
:raket:

IDE En det!

Henter emner fra en vektor

You can retrieve items from a vector using get. This is equivalent to using bracket notation to access items in an array in many imperative languages. Items in a vector are 0-indexed, counting from the left.

var arr = [1, 2, 3, 4, 5]; arr[0]; // => 1

In Clojure, this would be written like so:

(def a-vector [1 2 3 4 5]) (get a-vector 0) ; => 1

You can also give get a default value, if you give it an index that isn’t in the array.

;; the list doesn't have 2147483647 elements, so it'll return a string instead. (get a-vector 2147483646 "sorry, not found!") ; => "sorry, not found!"

Converting other collections into vectors

Non-vector data structures can be converted into vectors using the vec function. With hashmaps, this produces a 2D vector containing pairs of keys and values.

(vec '(1 2 3 4 5)) ; => [1 2 3 4 5] (vec {:jack "black" :barry "white"}) ; => [[:jack "black"] [:barry "white"]]

When to use a vector?

En vektor skal bruges i næsten alle tilfælde, hvis du har brug for en samling, fordi de har de korteste tilfældige adgangstider, hvilket gør det let at hente emner fra en vektor. Bemærk, at vektorer bestilles. Hvis ordren ikke betyder noget, kan det være bedre at bruge et sæt. Bemærk også, at vektorer er designet til at tilføje emner; hvis du har brug for at forberede varer, kan du bruge en liste.

Mere info om lukninger:

  • Lær JavaScript-lukninger på seks minutter
  • En grundlæggende guide til lukning i JavaScript
  • Oplev kraften i lukninger i VueJS
  • JavaScript-lukninger forklares ved mailing af en pakke