Zárások a JavaScript-ben, példákkal magyarázva

Mik azok a bezárások?

A bezárás egy funkció és a lexikális környezet (hatókör) kombinációja, amelyen belül a funkciót deklarálták. A bezárások a Javascript alapvető és erőteljes tulajdonsága. Ez a cikk a bezárásokkal kapcsolatos "hogyan" és "miért" témákat tárgyalja:

Példa

//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

Egy másik példa

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 } ]

A bezárás „emlékszik” arra a környezetre, amelyben létrejött. Ez a környezet minden olyan helyi változóból áll, amelyek a bezárás létrehozásakor hatálya alá tartoztak.

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 

A bezárások azért hasznosak, mert lehetővé teszik az adatok „emlékezését”, majd a visszaküldött függvények használatát. Ez lehetővé teszi a javascript számára, hogy más programozási nyelvekben található privát módszereket utánozzon. A privát módszerek hasznosak a kódhoz való hozzáférés korlátozásában, valamint a globális névtér kezelésében.

Privát változók és módszerek

A bezárások privát adatok / módszerek beágyazására is használhatók. Vessen egy pillantást erre a példára:

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

Ebben a példában balancea bankAccountfunkción kívülről nem tudunk bárhonnan hozzáférni , ami azt jelenti, hogy most létrehoztunk egy privát változót. Hol van a bezárás? Nos, gondold át, mi bankAccount()tér vissza. Valójában visszaad egy Objektumot, benne egy csomó függvénnyel, és amikor hívunk account.getBalance(), a funkció képes „megjegyezni” a kezdeti hivatkozást balance. Ez a bezárás ereje, ahol egy függvény „emlékezik” a lexikális hatókörére (fordítási időkör), még akkor is, ha a függvényt ezen a lexikális körön kívül hajtják végre.

Blokk hatókörű változók emulálása.

A Javascript nem rendelkezett blokk hatókörű változók fogalmával. Ez azt jelenti, hogy például egy forloopon belüli változó definiálásakor ez a változó a forloopon kívülről is látható. Tehát hogyan segíthetnek a bezárások a probléma megoldásában? Lássuk.

 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 }

Mivel az i változó nem rendelkezik blokk hatókörrel, mindhárom függvény értékét frissítettük a ciklusszámlálóval, és rosszindulatú értékeket hoztak létre. A bezárás segíthet abban, hogy megoldjuk ezt a problémát, ha pillanatképet készítünk arról a környezetről, amelyben a funkció létrehozásakor volt, megőrizve állapotát.

 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 }

A javascript es6 + késõbbi verzióiban van egy új let nevû kulcsszó, amellyel blokk hatókört adhatunk a változónak. Sok olyan funkció (forEach) és teljes könyvtár (lodash.js) is létezik, amelyek a fentiekben ismertetett problémák megoldására szolgálnak. Minden bizonnyal növelhetik a termelékenységet, azonban továbbra is rendkívül fontos, hogy ismerjük ezeket a kérdéseket, amikor valami nagy dolgot próbálunk létrehozni.

A bezárásoknak számos speciális alkalmazásuk van, amelyek hasznosak nagy javascript programok létrehozásakor.

  1. Privát változók emulálása vagy beágyazás
  2. Aszinkron szerveroldali hívások kezdeményezése
  3. Blokk hatókörű változó létrehozása.

Privát változók utánzása.

Sok más nyelvtől eltérően a Javascript nem rendelkezik olyan mechanizmussal, amely lehetővé tenné az objektumon belüli beágyazott példányváltozók létrehozását. Közönséges változók megléte sok problémát okozhat közepes és nagy programok felépítésekor. A bezárásokkal azonban ez a probléma mérsékelhető.

Az előző példához hasonlóan olyan funkciókat is készíthet, amelyek objektum literálokat adnak vissza olyan módszerekkel, amelyek hozzáférés nélkül hozzáférnek az objektum helyi változóihoz, anélkül, hogy azokat kitennék. Így tényleges magánjellegűvé téve őket.

A bezárások segíthetnek a globális névtér kezelésében is, hogy elkerüljék az ütközéseket a globálisan megosztott adatokkal. Általában az összes globális változó meg van osztva a projekt összes szkriptje között, ami mindenképpen rengeteg problémát okoz Önnek közepes és nagy programok felépítésekor. Ezért a könyvtár és a modulok szerzői bezárásokkal rejtik el a teljes modul módszereit és adatait. Ezt modulmintának hívják, egy azonnal meghívott függvénykifejezést használ, amely csak bizonyos funkciókat exportál a külvilágba, jelentősen csökkentve a globális referenciák mennyiségét.

Itt egy rövid minta a modulvázról.

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

A bezárások hasznosak az „emlékezetes” környezetben található magánváltozók új példányainak rögzítéséhez, és ezekhez a változókhoz csak a visszatérő függvény vagy módszerek férhetnek hozzá.

Vektorok

A vektor talán a legegyszerűbb gyűjtéstípus a Clojure-ban. Úgy gondolhat rá, mint egy tömbre a Javascriptben. Határozzunk meg egy egyszerű vektort:

(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])

Látni fogja, hogy szögletes zárójelet használ, akárcsak egy tömb a JS-ben. Mivel a Clojure, hasonlóan a JS-hez, dinamikusan van tipizálva, a vektorok bármilyen típusú elemeket befogadhatnak, beleértve más vektorokat is.

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

Elemek hozzáadása egy vektorhoz

A elemekkel hozzáadhat elemeket egy vektorhoz conj. Előfordulhat, hogy egy listát is használ into, de vegye figyelembe, hogy intokét vektor egyesítésére szolgál, tehát mindkét argumentumának vektornak kell lennie, és a használata intolassabb, mint a használata 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]
:rakéta:

IDEOne!

Elemek lekérése egy vektorból

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?

Ha gyűjteményre van szükség, akkor szinte minden esetben vektort kell használni, mivel ezek a legrövidebb véletlen hozzáférési időkkel rendelkeznek, ami megkönnyíti az elemek lekérését egy vektorból. Vegye figyelembe, hogy a vektorok rendezettek. Ha a sorrend nem számít, jobb lehet készletet használni. Vegye figyelembe azt is, hogy a vektorokat elemek csatolására tervezték; ha előre meg kell adnia az elemeket, érdemes listát használni.

További információ a bezárásokról:

  • Ismerje meg a JavaScript bezárását hat perc alatt
  • Alapvető útmutató a lezárásokhoz a JavaScript-ben
  • Fedezze fel a bezárások erejét a VueJS-ben
  • A JavaScript bezárása egy csomag postázásával magyarázható