Î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.
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.
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.
Î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.
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
.
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:
var
cuvântul cheie este utilizat. 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.
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.
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?
Î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);
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.
Î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.
Ca un bonus suplimentar, să aruncăm o privire la câteva utilizări ale închiderilor în sălbăticie.
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);
Î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ă);
O recapitulare rapidă a ceea ce am învățat:
Mulțumesc mult pentru citire! Simțiți-vă liber să puneți întrebări. Acum să ne bucurăm de petrecerea de pizza!