Domeniul de aplicare sau setul de reguli care determină locația variabilelor dvs. reprezintă unul dintre conceptele cele mai de bază ale oricărui limbaj de programare. Este atât de fundamental, de fapt, că este ușor să uități cât de subtile pot fi regulile!
Înțelegerea exactă a modului în care motorul JavaScript "gândește" cu privire la domeniul de aplicare vă va împiedica să scrieți bug-urile comune pe care le poate provoca ridicarea, să vă pregătească să vă înfășurați capul în jurul închiderilor și să vă apropiați cât mai mult de scrierea erorilor vreodată din nou.
... Ei bine, vă va ajuta să înțelegeți ridicarea și închiderea, oricum.
În acest articol, vom arunca o privire la:
lăsa
și const
schimbați joculHai să ne aruncăm.
Dacă sunteți interesat să aflați mai multe despre ES6 și cum să folosiți sintaxa și funcțiile pentru a îmbunătăți și simplifica codul JavaScript, de ce să nu verificați aceste două cursuri:
Dacă ați scris deja o linie de JavaScript înainte, veți ști asta unde tu defini variabilele determină unde puteți utilizare lor. Faptul că vizibilitatea unei variabile depinde de structura codului dvs. sursă se numește lexical domeniu.
Există trei moduri de a crea domeniul de aplicare în JavaScript:
lăsa
sau const
în interiorul unui bloc de cod. Aceste declarații sunt vizibile numai în interiorul blocului. captură
bloc. Credeți-vă sau nu, de fapt face creați un nou domeniu de aplicare!"folosiți stricte"; var mr_global = "Domnul Global"; funcția foo () var mrs_local = "Doamna locală"; console.log ("Pot vedea" + mr_global + "și" + mrs_local + ""); funcția bar () console.log ("De asemenea, pot vedea" + mr_global + "și" + mrs_local + ""); foo (); // Funcționează conform așteptărilor încercați console.log ("Dar / I / nu pot vedea" + mrs_local + "."); captură (err) console.log ("Ați primit doar" + err + ".); lăsați foo = "foo"; const bar = "bar"; console.log ("Pot folosi" + foo + bar + "în blocul său ..."); încercați console.log ("Dar nu în afara acestuia"); captură (err) console.log ("Ați primit încă un" + err + "."); // Throws ReferenceError! console.log ("Rețineți că" + err + "nu există în afara" capturii "!)
Fragmentul de mai sus demonstrează toate cele trei mecanisme de aplicare. Puteți rula în Nod sau Firefox, dar Chrome nu se joacă frumos cu lăsa
, inca.
Vom vorbi despre fiecare dintre ele în detalii deosebite. Să începem cu o analiză detaliată a modului în care JavaScript stabilește ce variabile aparțin domeniului.
Când rulați o bucată de JavaScript, două lucruri se întâmplă să o facă să funcționeze.
Pe parcursul compilare Etapa, motorul JavaScript:
Numai în timpul execuţie că motorul JavaScript stabilește, de fapt, valoarea referințelor variabile egale cu valorile lor de atribuire. Până atunci, sunt nedefinit
.
// Pot folosi first_name oriunde în acest program var first_name = "Peleke"; popup de funcții (first_name) // Pot folosi doar last_name în interiorul acestei funcții var last_name = "Sengstacke"; alertă (first_name + "+ last_name); popup (first_name);
Să facem pasul cu ceea ce face compilatorul.
Mai întâi, citește linia var first_name = "Peleke"
. Apoi, determină ce domeniu pentru a salva variabila la. Pentru că suntem la cel mai înalt nivel al scenariului, realizăm că suntem în la nivel global. Apoi, salvează variabila Nume
la domeniul global și inițiază valoarea acestuia nedefinit
.
În al doilea rând, compilatorul citește linia cu funcția popup (first_name)
. Deoarece funcţie cuvântul cheie este primul lucru pe linie, creează un nou domeniu de aplicare al funcției, înregistrează definiția funcției în sfera globală și caută în interior pentru a găsi declarații variabile.
Destul de sigur, compilatorul găsește unul. De când avem var last_name = "Sengstacke"
în prima linie a funcției noastre, compilatorul salvează variabila numele de familie
la scopul Pop-up
-nu la domeniul global - și își stabilește valoarea nedefinit
.
Deoarece în interiorul funcției nu mai există declarații variabile, compilatorul revine în domeniul global. Și deoarece nu mai există declarații variabile Acolo, această fază se face.
Rețineți că nu am avut de fapt alerga nimic încă. Funcția compilatorului în acest moment este doar să vă asigurați că știe numele tuturor; nu-i pasă ce ei fac.
În acest moment, programul nostru știe că:
Nume
în domeniul global.Pop-up
în domeniul global.numele de familie
în domeniul de aplicare al Pop-up
.Nume
și numele de familie
sunteți nedefinit
.Nu ne pasă că am atribuit valorile acestor variabile în altă parte a codului nostru. Motorul are grija de asta execuţie.
În timpul următorului pas, motorul ne citește din nou codul, dar de data aceasta, Executa aceasta.
Mai întâi, citește linia, var first_name = "Peleke"
. Pentru a face acest lucru, motorul caută variabila numită Nume
. Deoarece compilatorul a înregistrat deja o variabilă cu numele respectiv, motorul îl găsește și își stabilește valoarea "Peleke"
.
Apoi, citește linia, funcția popup (first_name)
. Din moment ce nu suntem executare funcția de aici, motorul nu este interesat și trece peste el.
În cele din urmă, citește linia pop-up (FIRST_NAME)
. De cand noi sunteți executând o funcție aici, motorul:
Pop-up
Nume
Pop-up
ca o funcție, trecerea valorii Nume
ca parametruCând se execută Pop-up
, trece prin același proces, dar de data aceasta în interiorul funcției Pop-up
. Aceasta:
numele de familie
numele de familie
valoarea lui egală cu "Sengstacke"
alerta
, executându-l ca o funcție cu "Peleke Sengstacke"
ca parametruSe pare că se petrec mult mai mult sub capotă decât ne-am fi gândit!
Acum că înțelegeți modul în care JavaScript citește și rulează codul pe care îl scrieți, suntem gata să abordăm ceva mai aproape de casă: cum funcționează ascensorul.
Să începem cu un cod.
bar(); funcția bar () if (! foo) alert (foo + "? Aceasta este ciudat ..."); var foo = "bar"; rupt (); // Eroare de scris! var broken = function () alert ("Această avertizare nu va apărea!");
Dacă executați acest cod, veți observa trei lucruri:
foo
înainte de a le atribui, dar valoarea lor este nedefinit
.spart
înainte de ao defini, dar veți obține o Eroare de scris
.bar
înainte de ao defini, și funcționează așa cum doriți.Ridicat se referă la faptul că JavaScript face disponibile toate numele de variabile declarate pretutindeni în domeniile lor - inclusiv inainte de le atribuim.
Cele trei cazuri din fragment sunt cele trei pe care va trebui să le cunoașteți în propriul cod, așa că vom trece prin fiecare unul câte unul.
Rețineți, atunci când compilatorul JavaScript citește o linie ca var foo = "bar"
, aceasta:
foo
la cel mai apropiat domeniu de aplicarefoo
la nedefinitMotivul pentru care putem folosi foo
înainte de a le atribui este pentru că, atunci când motorul caută variabila cu acel nume, acesta face exista. De aceea nu arunca a ReferenceError
.
În schimb, devine valoarea nedefinit
, și încearcă să o folosească pentru a face ceea ce ați cerut. De obicei, este un bug.
Ținând cont de acest lucru, ne-am putea imagina ceea ce vede JavaScript în funcția noastră bar
este mai mult ca aceasta:
funcția bar () var foo; // undefined if (! foo) //! undefined este adevărat, alertă atât de alertă (foo + "" Aceasta este ciudată ... "); foo = "bar";
Acesta este Prima regulă de ridicare, dacă: Variabilele sunt disponibile în întregul lor domeniu de aplicare, dar au valoarea nedefinit
până când codul dvs. îi va atribui.
Un idiom comun JavaScript este de a scrie toate dvs. var
declarații în partea de sus a domeniului lor de aplicare, în loc de locul în care le folosiți pentru prima dată. Pentru a paraframa Doug Crockford, acest lucru vă ajută codul citit mai mult ca asta ruleaza.
Când te gândești la asta, are sens. Este destul de clar de ce bar
se comportă așa cum o face atunci când scriem codul nostru așa cum îl citește JavaScript, nu-i așa? Deci, de ce nu scrieți așa toate timpul?
Faptul că avem a Eroare de scris
când am încercat să executăm spart
înainte de a ne defini că este doar un caz special al primei reguli de ridicare.
Am definit o variabilă, numită spart
, pe care compilatorul o înregistrează în domeniul global și stabilește egal cu nedefinit
. Când încercăm să o executăm, motorul caută valoarea spart
, constată că este nedefinit
, și încearcă să execute nedefinit
ca o funcție.
Evident, nedefinit
nu este o funcție - de aceea avem un a Eroare de scris
!
În cele din urmă, amintiți-vă că am reușit să sunăm bar
înainte de ao defini. Acest lucru se datorează A doua regulă de ridicare: Atunci când compilatorul JavaScript găsește o declarație a funcției, aceasta face atât numele său și definiție disponibilă în partea de sus a domeniului său de aplicare. Rescrierea codului nostru încă o dată:
funcția bar () if (! foo) alert (foo + "? Aceasta este ciudat ..."); var foo = "bar"; var rupt; // undefined bar (); // barul este deja definit, execută bine rupt (); // Nu se poate executa nedefinit! broken = function () alert ("Această avertizare nu va apărea!");
Din nou, are mult mai mult sens când tu scrie ca JavaScript citește, nu crezi?
Pentru a revizui:
nedefinit
până la alocare.Acum, să aruncăm o privire asupra a două instrumente noi care funcționează puțin diferit: lăsa
și const
.
lăsa
, const
, & Zona Dead Temporală Spre deosebire de var
declarații, variabile declarate cu lăsa
și const
nu face să fie ridicată de compilator.
Cel puțin, nu exact.
Amintiți-vă cum am putut să sunăm spart
, dar a primit o Eroare de scris
pentru că am încercat să executăm nedefinit
? Dacă am fi definit spart
cu lăsa
, am fi ajuns ReferenceError
, in schimb:
"folosiți stricte"; // Trebuie să utilizați "stricte" pentru a încerca acest lucru în Nod rupt (); // ReferenceError! let broken = function () alert ("Această avertizare nu va apărea!");
Atunci când compilatorul JavaScript înregistrează variabilele la domeniile lor în prima sa trecere, acesta tratează lăsa
și const
diferit decât face var
.
Când găsește a var
declarație, vom înregistra numele variabilei în domeniul său de aplicare și imediat vom inițializa valoarea acesteia nedefinit
.
Cu lăsa
, cu toate acestea, compilatorul face înregistrați variabila în domeniul său de aplicare, dar nuinițializați valoarea acestuia nedefinit
. În schimb, ea lasă variabila neinitializată, pana cand motorul execută instrucțiunea de atribuire. Accesarea valorii unei variabile neinitializate aruncă a ReferenceError
, ceea ce explică de ce fragmentul de mai sus aruncă atunci când îl executăm.
Spațiul dintre începutul părții superioare a domeniului lăsa
declarația și instrucțiunea de atribuire se numește Zona Dead Zone temporară. Numele vine de la faptul că, chiar dacă motorul știe despre o variabilă numită foo
în partea de sus a domeniului de aplicare al bar
, variabila este "moartă", deoarece nu are valoare.
De asemenea, pentru că vă va ucide programul dacă încercați să îl utilizați mai devreme.
const
cuvântul cheie funcționează la fel ca lăsa
, cu două diferențe cheie:
const
.const
.Acest lucru garantează acest lucru const
voi mereuau valoarea pe care ați atribuit-o inițial.
// Aceasta este legala const React = require ('react'); // Aceasta nu este în întregime legală cri cripto; crypto = necesită ('crypto');
lăsa
și const
sunt diferite de var
într-un alt mod: dimensiunea domeniilor lor.
Când declarați o variabilă cu var
, este vizibil cât mai sus în lanțul de aplicare posibil - de obicei, în partea superioară a celei mai apropiate declarații de funcții sau în sfera globală, dacă îl declarați la nivel superior.
Când declarați o variabilă cu lăsa
sau const
, totuși, este vizibil ca la nivel local pe cat posibil-numai în cel mai apropiat bloc.
A bloc este o secțiune a codului declanșată de acolade, așa cum vedeți cu dacă
/altfel
blocuri, pentru
bucle și în blocuri explicite "blocate" de cod, ca în acest fragment.
"folosiți stricte"; lăsați foo = "foo"; dacă (foo) const bar = "bar"; var foobar = foo + bar; console.log ("Pot vedea" + bar + "în acest bloc."); try console.log ("Pot vedea" + foo + "în acest bloc, dar nu" + bar + ".); captură (err) console.log ("Ați primit" + err + ".); încercați console.log (foo + bar); // Aruncă din cauza "foo", dar ambele sunt nedefinite catch (err) console.log ("Ați primit doar" + err + ".); console.log (foobar); // Merge bine
Dacă declarați o variabilă cu const
sau lăsa
în interiorul unui bloc, este numai vizibil în interiorul blocului și numai după ce l-ați desemnat.
O variabilă declarată cu var
, este totuși vizibilă cât mai departe posibil-în acest caz, în domeniul global.
Dacă sunteți interesat de detaliile de tip dură lăsa
și const
, verificați ce are de spus Dr. Rauschmayer despre ei în Explorarea ES6: Variabile și scopuri și aruncați o privire la documentația MDN pe ele.
acest
& Funcții cu săgețiLa suprafață, acest
nu pare să aibă o mulțime de a face cu domeniul de aplicare. Și, de fapt, JavaScript nu nu rezolva sensul acest
în conformitate cu regulile de domeniu pe care le-am vorbit aici.
Cel puțin, nu de obicei. JavaScript, notoriu, nu nu rezolva sensul acest
cuvânt cheie bazat pe locul în care l-ați folosit:
var foo = nume: 'Foo', limbi: ['spaniolă', 'franceză', 'italiană'], vorbesc: functie vorbesc () this.languages.forEach (funcția (limbă) console.log nume + "vorbește" + limbă + ".");); foo.speak ();
Cei mai mulți dintre noi s-ar aștepta acest
a insemna foo
în interiorul pentru fiecare
buclă, pentru că asta însemna chiar în afara ei. Cu alte cuvinte, ne așteptăm ca JavaScript să rezolve semnificația acest
lexical.
Dar nu.
În schimb, creează o nou acest
în interiorul fiecărei funcții pe care o definiți și decide ce înseamnă pe el Cum numiți funcția - nu Unde ai definit-o.
Acest prim punct este similar cu cel al redefinirii orice variabilă în domeniul copilului:
funcția foo () var bar = "bar"; function baz () // Reutilizarea numelor de variabile ca aceasta se numește "shadowing" var bar = "BAR"; console.log (bar); // BAR baz (); foo (); // BAR
A inlocui bar
cu acest
, și totul ar trebui să se limpezească instantaneu!
În mod tradițional, obținerea acest
să lucrăm așa cum ne așteptăm ca variabilele vechi lexicale vechi obișnuite să funcționeze necesită una din cele două soluții:
var foo = nume: 'Foo', limbi: ['spaniolă', 'franceză', 'italiană'], speak_self: funcția speak_s () var self = this; auto.languages.forEach (functie (limbă) console.log (auto.name + "vorbește" + limbă + ".");), speak_bound: funcția speak_b () this.languages.forEach ) console.log (acest nume + "vorbește" + limbă + "."); .bind (foo)); // Mai frecvent: .bind (acest lucru); ;
În speak_self
, am salvat sensul acest
la variabila de sine
, si foloseste acea pentru a obține referința pe care o dorim. În speak_bound
, folosim lega
la permanent punct acest
la un obiect dat.
ES2015 ne aduce o nouă alternativă: funcțiile săgeților.
Spre deosebire de funcțiile "normale", funcțiile săgeților fac nu umbra scopurilor părintelui lor acest
valoare prin stabilirea propriilor lor. Mai degrabă, ei își rezolvă semnificația lexical.
Cu alte cuvinte, dacă folosiți acest
într-o funcție de săgeată, JavaScript își caută valoarea așa cum ar fi orice altă variabilă.
În primul rând, verifică domeniul de aplicare local pentru o acest
valoare. Deoarece funcțiile săgeților nu stabilesc una, nu va găsi una. Apoi, verifică mamă domeniul de aplicare pentru o acest
valoare. Dacă găsește una, va folosi asta, în schimb.
Acest lucru ne permite să rescrieți codul de mai sus astfel:
var foo = nume: 'Foo', limbi: ['spaniolă', 'franceză', 'italiană'], vorbesc: function speak () this.languages.forEach ((language) .name + "vorbește" + limbă + "."););
Dacă doriți mai multe detalii cu privire la funcțiile săgeților, aruncați o privire la cursul excelent al lui Envato Tuts + Instructor Dan Wellman privind JavaScript ES6 Fundamentals, precum și documentația MDN privind funcțiile săgeților.
Am acoperit mult teren până acum! În acest articol, ați aflat că:
lăsa
sau const
înainte de alocare aruncă a ReferenceError
, și că astfel de variabile se referă la cel mai apropiat bloc.acest
, și ocolind legarea dinamică tradițională.De asemenea, ați văzut cele două reguli de ridicare:
var
declarațiile sunt disponibile în toate domeniile în care sunt definite, dar au valoarea nedefinit
până când executa declarațiile de atribuire.Un pas bun următor este să vă folosiți cunoștințele de bază ale domeniului JavaScript pentru a vă împacheta capul în jurul închiderilor. Pentru asta, verificați Scopes & Closures lui Kyle Simpson.
În sfârșit, mai sunt multe de spus despre asta acest
decât am putut să acopăr aici. În cazul în care cuvântul cheie pare încă o magie neagră, aruncați o privire asupra acestei prototipuri Object Prototypes pentru a obține capul în jurul său.
În același timp, ia ceea ce ați învățat și mergeți să scrieți mai puține bug-uri!
Aflați JavaScript: Ghidul complet
Am creat un ghid complet care vă ajută să învățați JavaScript, indiferent dacă începeți doar ca dezvoltator web sau doriți să explorați subiecte mai avansate.