Închizături față în spate

Închiderile sunt adesea văzute ca o artă arcane în țara JavaScript. Odată stăpânite, vă permit să scrieți niște JavaScript cu adevărat uimitoare. Acest articol vă va ajuta să accelerați magia închiderii JavaScript.


Ce este o închidere?

Unul dintre adevărurile cheie ale JavaScript este acela Tot este un obiect. Aceasta, desigur, include funcții.

O închidere nu este nimic mai mult decât un obiect de funcție cu un domeniu de legătură în care variabilele funcției sunt rezolvate.

Închiderea își ia numele din cauza modului în care acestea închide asupra conținutului acestora. Luați în considerare următorul fragment de JavaScript:

 topping = "anchovi"; funcția pizzaParty (numSlices) var topping = "pepperoni", innerFunction = funcția () var topping = "șuncă"; console.log ("... dar pune" + topping + "pe" + numSlices + "felii"); ; console.log ("Această pizza este despre" + topping "); innerFunction ();  pizzaParty (3);

Dacă vă deschideți consola preferată și alergați băiatul rău, veți fi întâmpinat cu un mesaj delicios, în sensul că "Această pizza se referă la peperoni ... Dar puneți sunca pe 3 felii." Acest exemplu ilustrează câteva concepte-cheie ale JavaScript care sunt esențiale pentru a obține o blocare a închiderilor.

O închidere este un obiect de funcții

Câte obiecte de funcții sunt în codul de mai sus? Ei bine ... avem pe noi pizzaParty funcția și imbricate în această funcție este innerFunction. Matematica nu a fost întotdeauna costumul meu puternic, dar 1 + 1 = 2 în cartea mea. Fiecare obiect de funcții are un set propriu de variabile, care sunt rezolvate în fiecare funcție domeniu.

O închidere are un domeniu de aplicare propriu

Închiderea nu poate fi înțeleasă pe deplin fără o întindere fermă. Mecanismul de aplicare al mecanismului JavaScript este ceea ce permite fiecărei funcții să aibă propria sa funcție creștet variabilă și fără ea s-ar putea să avem prea mulți pepperoni, prea puțin șuncă sau să arunci * niște hamsii la petrecerea noastră de pizza. Să folosim o ilustrare rapidă pentru a ilustra mai bine această idee.

Funcțiile sunt executate utilizând domeniul de aplicare care a fost în vigoare atunci când funcția a fost definită. Nu are nimic de-a face cu domeniul în vigoare când se numește funcția.

Variabilitatea accesibilității funcționează în exterior

Săgețile verzi arată că accesibilitatea funcționează din exterior. Variabilele definite în domeniul de aplicare în afara unei funcții sunt accesibile din interiorul acesteia.

Dacă o să omitem creștet variabilă din interior pizzaParty funcția, atunci vom primi un mesaj ca "Această pizza este totul despre anchovi", dar din moment ce pizzaParty are o creștet variabilă în domeniul său de aplicare; cei dragi săraci nu se vor apropia niciodată de petrecerea noastră de pizza.

De asemenea, numSlices parametrul poate fi accesat din interior innerFunction deoarece este definită în domeniul de aplicare de mai sus - în acest caz domeniul de aplicare al directivei pizzaParty.

Accesibilitatea variabilă nu funcționează în interior

Săgețile roșii arată că variabilele din domeniul de aplicare pentru o funcție nu sunt niciodată accesibile în afara funcției respective. Acesta este cazul numai atunci când o variabilă îndeplinește una dintre următoarele condiții:

  1. var cuvântul cheie este utilizat.
  2. Variabila este un parametru pentru funcția sau o funcție externă.
  3. Variabila este o funcție imbricată.

Omiterea var atunci când setați o variabilă, JavaScript va determina setarea celei mai apropiate variabile numite în funcțiile exterioare până la domeniul global. Deci, folosind exemplul nostru, sunca creștet în innerFunction nu pot fi accesate de la pizzaParty, și peperoni creștet în pizzaParty nu pot fi accesate în sfera globală în care locuiesc anchovii.

JavaScript folosește Lexical Scooping

Sfera de aplicare Lexical înseamnă că funcțiile sunt executate utilizând variabila în vigoare atunci când funcția a fost definit. Nu are nimic de-a face cu domeniul în vigoare când se numește funcția. Acest fapt este crucial pentru deblocarea puterii închiderilor.

Acum, că înțelegem ce înseamnă închiderea și ce scop înseamnă închideri, să ne aruncăm în câteva cazuri clasice de utilizare.


Utilizarea închiderilor pentru confidențialitate

Sunt închise mod de a vă ascunde codul din ochiul public. Cu închideri, puteți avea cu ușurință membri privați care sunt protejați de lumea exterioară:

 (funcția (exportul) funcția myPrivateMultiplyFunction (num, num2) return num * num2; // echivalentă cu window.multiply = funcția (num1, num2) ... exports.multiply = (funcția myPrivateMultiplyFunction (num1, num2));) (fereastră);

Cu închideri, puteți avea cu ușurință membri privați care sunt protejați de lumea exterioară.

Să-l rupem. Obiectul funcției noastre de nivel superior este o funcție anonimă:

 (funcția (exporturi) ) (fereastră);

Invocăm imediat această funcție anonimă. Transmitem contextul global (fereastră în acest caz), astfel încât să putem "exporta" o funcție publică, dar să ascundem orice altceva. Deoarece funcția myPrivateMultiplyFunction este o funcție imbricată, există doar în limitele scopului închiderii noastre; astfel încât să o putem folosi oriunde în acest domeniu, și numai în acest domeniu.

JavaScript va menține o referință la funcția noastră privată pentru utilizarea în interiorul funcției de multiplicare, dar myPrivateMultiplyFunction nu pot fi accesate în afara închiderii. Să încercăm acest lucru:

 multiplica (2,6) // => 12 myPrivateMultiplyFunction (2,6) // => ReferenceError: myPrivateMultiplyFunction nu este definita

Închiderea ne-a permis să definim o funcție pentru uz privat, permițându-ne totodată să controlam ceea ce vede restul lumii. Ce altceva pot face închiderile?


Folosirea închiderilor pentru meta-programare

Închidere sunt destul de la îndemână atunci când vine vorba de generarea de cod. Obosit de amintirea tuturor acelor coduri de chei plictisitoare pentru evenimentele tastaturii? O tehnică comună este folosirea unei hărți cheie:

 var KeyMap = "Enter": 13, "Shift": 16, "Tab": 9, "LeftArrow": 37;

Apoi, în evenimentul de la tastatură, dorim să verificăm dacă a fost apăsată o anumită cheie:

 var txtInput = document.getElementById ('myTextInput'); txtInput.onkeypress = funcția (e) var code = e.keyCode || e // care // tariful obișnuit pentru obținerea cheii presate dacă (code === KeyMap.Enter) console.log (txtInput.value); 

Capturarea unui moment în timp

Exemplul de mai sus nu este cel mai rău, dar putem folosi meta-programarea și închiderile pentru a face o soluție și mai bună. Folosind cele existente Hartă taste obiect, putem genera câteva funcții utile:

 pentru (var key în KeyMap) // accesați obiectul cu accesoriu array pentru a seta numele funcției "dyanamic" KeyMap ["este" + key] = (funcția (compare) var code = ev.keyCode | | ev.which; codul de returnare === compare;) (KeyMap [key]); 

Închiderea este atât de puternică deoarece poate capta variabilele locale și legăturile parametrilor funcției în care sunt definite.

Această buclă generează o este funcție pentru fiecare tastă Hartă taste, și al nostru txtInput.onkeypress funcția devine un pic mai ușor de citit:

 var txtInput = document.getElementById ('myTextInput'); txtInput.onkeypress = funcția (e) if (KeyMap.isEnter (e)) console.log (txtInput.value); 

Magia începe aici:

 KeyMap ["este" + cheie] = (funcție (compara) ) (KeyMap [cheie]); // invocați imediat și treceți valoarea curentă la KeyMap [key]

Așa cum am bucle peste cheile în Hartă taste, transferăm valoarea pe care o face referire cheia respectivă la funcția externă anonimă și o invocăm imediat. Aceasta leagă acea valoare la comparaţie parametru al acestei funcții.

Închiderea de care suntem interesați este cea pe care o întoarcem din interiorul funcției anonime:

 returnează funcția (ev) var code = ev.keyCode || ev.which; codul returnat === compare; 

Amintiți-vă, funcțiile sunt executate cu scopul care a fost în vigoare atunci când au fost definite. comparaţie parametru este legat de Hartă taste valoare care a fost în vigoare în timpul unei iterații de buclă, astfel încât închiderea noastră imbricată este capabilă să o captureze. Luăm un instantaneu în timp, care a avut un efect în acel moment.

Funcțiile pe care le-am creat ne permit să ignorăm configurarea cod variabilă de fiecare dată când vrem să verificăm codul cheie și acum avem funcții ușor de citit și ușor de utilizat.


Folosirea închiderilor pentru a extinde limbajul

În acest moment, ar trebui să fie relativ ușor de văzut că închiderile sunt vitale pentru scrierea de top notch JavaScript. Să aplicăm ceea ce știm despre închideri pentru a mări un tip nativ JavaScript (gasp!). Cu accentul pe obiectele de funcții, să lărgim pe cei nativi Funcţie tip:

 Function.prototype.cached = function () var self = aceasta, // "aceasta" se refera la cache-ul functiei originale = ; // funcția noastră de returnare a spațiului de stocare în cache local (cu arhive) if (args in cache) return cache [args]; cache retur [args] = auto (args); ; ;

Această bijuterie mică permite oricărei funcții să creeze o versiune stocată în cache. Puteți vedea că funcția returnează o funcție în sine, astfel încât acest accesoriu poate fi aplicat și folosit ca atare:

 Math.sin = Math.sin.cached (); Math.sin (1) // => 0.8414709848078965 Math.sin (1) // => 0.8414709848078965 de data aceasta scos din cache

Observați abilitățile de închidere care intră în joc. Avem un local ascunzătoare variabilă care este păstrată privată și protejată de lumea exterioară. Acest lucru va împiedica orice manipulare care ar putea invalida memoria cache.

Închiderea returnată are acces la legăturile funcției externe, ceea ce înseamnă că suntem capabili să returnez o funcție cu acces complet la memoria cache-ului din interior, precum și funcția originală! Această funcție mică poate face minuni pentru performanță. Această extensie specială este configurată pentru a gestiona un argument, dar mi-ar plăcea să văd înjunghierea dvs. la o funcție cache cu mai multe argumente.


Închizăturile în sălbăticie

Ca un bonus suplimentar, să aruncăm o privire la câteva utilizări ale închiderilor în sălbăticie.

jQuery

Uneori, celebrul jQuery $ fabrica nu este disponibilă (gândiți WordPress) și dorim să o folosim în modul pe care îl facem în mod obișnuit. Mai degrabă decât să ajungă la jQuery.noConflict, putem folosi o închidere pentru a permite ca funcțiile din interior să aibă acces la noi $ legarea parametrilor.

 (funcția ($) $ (document) .ready (funcția () // business as usual ...);) (jQuery);

Backbone.js

În proiectele mari de Backbone.js, este posibil ca modelele dvs. de aplicații să fie private și apoi să expuneți un API public pe vizualizarea principală a aplicației. Folosind o închidere, puteți obține cu ușurință această confidențialitate.

 (funcția (exportul) var Product = Backbone.Model.extend (urlRoot: '/ products',) var ProductList = Backbone.Collection.extend (url: '/ products' Produse = noul ProductList; var ShoppingCartView = Backbone.View.extend (addProduct: function (product, opts) returneaza CartItems.create (product, opts); , optiuni);, getProduct: function (productId) retur Products.get (productId);, getProducts: function () return Products.models; noul ShoppingCartView;) (fereastră);

Concluzie

O recapitulare rapidă a ceea ce am învățat:

  • O închidere nu este altceva decât un obiect de funcție cu un domeniu de aplicare.
  • Închidere a lua numele lor de modul în care "închide" peste conținutul lor.
  • Încheie numerar în timp util în domeniul lexical al JavaScript.
  • Închiderea este calea de a obține confidențialitatea în JavaScript.
  • Închizățile pot capta variabilele locale și legăturile parametrilor unei funcții externe.
  • JavaScript poate fi extins puternic cu unele magie de închidere.
  • Închiderea poate fi utilizată cu multe dintre bibliotecile dvs. preferate pentru a le face mai reci!

Mulțumesc mult pentru citire! Simțiți-vă liber să puneți întrebări. Acum să ne bucurăm de petrecerea de pizza!

Cod