JavaScript este ECMAScript
INTRODUCERE IN JavaScript
De ce sa inveti JavaScript
JavaScript este un limbaj de programare usor de adaugat in documentele HTML, suportat de toate browserele si reprezinta pasul urmator dupa ce ai invatat limbajele HTML si CSS. Orice grafica dintr-o pagina web indiferent de cat de bine arata, este aproape inutila daca nu putem interactiona cu utilizatorul anticipand diferite actiuni (evenimente) ale acestuia intr-o pagina web.
Cateva actiuni pe care le putem face intr-o pagina web sunt prezentate in lista urmatoare:
- putem actualiza/sterge un continut text/imagine/audio/video
- putem schimba fontul/dimensiunea/culoarea unui text
- putem incarca asincron continut nou in pagina fara a o reincarca
- putem face o prima validare a unui formular inainte ca acesta sa fie trimis catre server
- putem crea elemente grafice pentru ca mai apoi sa le adaugam in eticheta HTML Canvas sau putem anima etichetele HTML SVG
- putem crea diferite unelte web prin care sa generam elemente grafice compuse din HTML si CSS
- putem afla ora si/sau data locala a utilizatorului paginii
- putem afla diferite informatii utile legate de browserul utilizatorului
- putem sa detectam limba browserului pentru a incarca continutul text in acea limba
- putem sa trimitem notificari in browser daca utilizatorul accepta acest lucru
- putem afla daca pagina HTML a fost incarcata complet pentru ca mai apoi sa executam diferite scenarii de cod care vor face ceva
- putem incarca cu ajutorul utilizatorului intr-un element din pagina, o imagine sau continutul text dintr-un fisier ce se afla pe masina de calcul a utilizatorului
- in browserele de pe dispozitivele mobile putem folosi diferite API-uri JavaScript prin care putem afla cat la suta mai are un utilizator in baterie, mai putem afla orientarea in cele trei axe a dispozitivului sau sa pornim functia de vibrare a dispozitivului
- putem crea diferite aplicatii, precum un editor de text online, o tastatura online, un selector de culoare, un generator de sunete sau chiar sa punem browserul sa citeasca un text
JavaScript si ECMA
Nucleul limbajului JavaScript si noile caracteristici sunt definite intr-un standard cu numele de ECMA-262. Limbajul definit in acest standard este denumit ECMAScript. Ceea ce stiti ca JavaScript este de fapt limbajul ECMAScript. Acest limbaj, care ruleaza in browsere si nu numai, contine diferite obiecte si metode personalizate care completeaza acest limbaj.
Scurt istoric
Drumul pana la ECMAScript 6
In anul 2007, JavaScript a fost la o rascruce de drumuri. Ajax a fost introdus intr-o noua era de aplicatii web dinamice. La acel moment JavaScript nu se schimbase inca de la editia a treia a ECMA-262, editie ce a fost publicata in anul 1999.
TC-39, comisia responsabila de conducerea procesului de dezvoltare a limbajului ECMAScript, a adaugat o specificatie extinsa a proiectelor pentru ECMAScript 4. Odata cu aceasta specificatie s-a dorit introducerea de modificari mici si mari in limbaj. Aceste modificari includeau sintaxa noua, module, clase, mostenirea clasica, membrii obiectului privat, adnotarile de tip optional si multe altele.
Toate aceste modificari au creat diferite discutii iar in final o ruptura intre membri TC-39. O parte dintre acestia au considerat ca prin noul standard ECMAScript 4 se dorea sa se realizeze prea mult.
Un grup de lideri de la gigantii Yahoo, Google si Microsoft au creat o alternativa pentru noua versiune pe care initial au denumit-o ECMAScript 3.1. Prin aceasta versiune s-a dorit doar o simpla incrementare adusa standardului existent.
Versiunea ECMAScript 3.1 aducea foarte putine modificari de sintaxa si asta pentru ca scopul principal a fost adaugarea de suport pentru JSON si diferite proprietati si metode in obiectele ce existau la acea vreme.
Desi versiunea ECMAScript 3.1 aducea doar o fractie din ceea ce s-a propus initial, a existat o incercare timida de a unifica ECMAScript 3.1 cu ECMAScript 4. Acest lucru nu s-a mai intamplat pentru ca ambele parti au avut diferite viziuni asupra modului la cum ar trebui dezvoltat in viitor limbajul ECMAScript.
In anul 2008, creatorul limbajului ECMAScript, Brendan Eich a anuntat ca TC-39 isi va concentra eforturile pentru a standardiza ECMAScript 3.1 pastrand caracteristicile majore din ECMAScript 4 pentru un standard viitor. Aceasta colaborare sau mai bine spus reconciliere a primit si un nume iar acesta este Armonia ECMAScript (en. ECMAScript Harmony).
Un an mai tarziu ECMAScript 3.1 a fost oficial standardizat in versiunea ECMAScript 5, scopul fiind acela de a nu creea diferite confuzii asupra versiunii limbajului. Odata cu lansarea versiunii cinci a limbajului s-a inceput munca pentru urmatoarea versiune iar ECMAScript 6 reprezinta prima lansare in spiritul armonios al ECMAScript Harmony.
ECMAScript 6 a fost oficial lansat in anul 2015 si din cauza ca a fost lansat in anul 2015 a primit inca o denumire, aceasta fiind ECMAScript 2015 insa toata lumea o stie sub denumirea de ECMAScript 6.
Noile caracteristici din ECMAScript 6 includ obiecte noi si sintaxa noua asupra obiectelor existente. Partea interesanta in ECMAScript 6 este ca s-au rezolvat diferite probleme pe care limbajul le avea si cu care marea majoritatea a programatorilor se confrunta.
PRIMII PASI IN JavaScript
Editoare de text si IDE
Scenariile de cod scrise in limbajul JavaScript pot fi scrise in orice editor de text ANSI disponibil implicit sau nu pe sistemul de operare folosit. Mai apoi, scenariul de cod va putea fi interpretat de interpretorul incorporat in browser.
Folosirea editoarelor de text ofera suport limitat pentru programator si nu poate detecta diferite erori, de orice fel ar fi ele.
IDE-urile sunt programe software ce pot fi instalate pe masina de calcul si reprezinta suportul suplimentar prin care un editor de text ajuta un programator sa detecteze diferite erori in timp ce scrie cod dar si suport facil in crearea unui proiect compus din mai multe fisiere scrise in unul sau mai multe limbaje de marcare ori programare. Cateva IDE-uri populare sunt Atom, Sublime Text sau Notepad++.
Ce ar trebui sa stii
Inainte de a invata JavaScript, este obligatoriu sa ai cunostinte de cel putin nivel mediu asupra limbajelor HTML si CSS. JavaScript reprezinta pasul urmator dupa ce ai invatat aceste limbaje.
Definitie sintaxa
Sintaxa reprezinta regulile de scriere al scenariului de cod intr-un limbaj de programare (C/C++, PHP, JavaScript etc.) sau marcare (XML, HTML, CSS etc.).
Cateva reguli de scriere al codului in JavaScript
Aceste reguli te vor ajuta sa eviti diferite erori ce vor apare daca nu le respecti. Nu este nici-o problema daca nu intelegi ceva acum, pe masura ce vei asimila cunostinte si competente, te poti intoarce oricand aici sa recitesti aceasta sectiune.
-
semantica
- numele de variabile si functii trebuie sa reflecte rolul acestora
- scrie numele variabilelor si al functiilor cu litera mica
- daca numele unei variabile sau al unei functii contine mai mult de un cuvant, aceste cuvinte pot fi comasate intr-unul mai mare iar fiecare litera incepand de la al doilea cuvant sa fie cu litera mare (ex. numeLungDeVariabila)
- prima litera din numele unei clase sa fie litera mare
- adauga un caracter spatiu inainte si dupa operatori
- indenteaza corespunzator fiecare bloc de cod - structuri conditionare/repetitive, functii, clase si obiecte
-
sintaxa
- adauga intotdeauna la sfarsitul unei declaratii caracterul punct si virgula
-
date primitive
- daca este folosit acelasi cuvant rezervat atunci cand sunt declarate mai multe variabile/constante, putem sa le separam numele si/sau valorile folosind operatorul virgula
-
obiecte
-
JSON
- adauga acolada de deschidere pe acelasi rand cu operatorul de atribuire
- "cheia" sau proprietatea poate fi adaugata in interiorul unei perechi de ghilimele
- adauga valoarea in interiorul ghilimelelor duble sau simple daca este vorba de un text
- adauga operatorul virgula dupa fiecare valoare a unei chei, mai putin in cazul ultimei valori
- intotdeauna sa adaugi acolada de inchidere a JSON-ului pe o alta linie
- adauga caracterul punct si virgula dupa acolada de inchidere a JSON-ului sau caracterul virgula daca urmeaza declararea unei alte variabile
-
JSON
Erori in limbaj
Erori de sintaxa
Sintaxa reprezinta setul de reguli pentru a scrie cod in limbajul dat. Erorile de sintaxa reprezinta incalcarea acestor reguli sau nerespectarea lor. In toate limbajele interpretate, verificarea sintaxei codului dintr-un fisier este realizata inainte de a se executa o linie de cod.
Cele mai intalnite erori de sintaxa
- omiterea acoladei, a parantezei rotunde sau a parantezei drepte de incheiere - } ) ]
- absenta ghilimelelor in declararea unei variabile sir sau a valorilor sub forma sirurilor dintr-o matrice
- declararea/folosirea incorecta a variabilelor - cu litera mica sau omiterea unei paranteze drepte
- variabile carora li s-au atribuit valori folosind operatorul de comparare si nu cel de atribuire
- comentarii pe mai multe linii ce nu au fost incheiate
- a fost omis caracterul punct si virgula la sfarsitul liniei de cod generand diferite erori la minificarea codului
- declararea incorecta a elementelor caracteristice limbajului - ex: intr-un switch s-a folosit gresit cuvintele cheie case si default la modul case default:
Erori de sintaxa in modul strict - Notiuni avansate
Modul strict presupune adaugarea sirului de caractere 'use strict' la inceputul scenariului de cod din scopul global sau local.
1. O functie nu poate avea ca argument un obiect literar iterabil.
function test(x = this){ 'use strict'; // eroare return x; } function test({x, y}){ 'use strict'; // eroare return x; }
2. Al doilea argument nu poate avea ca valoare implicita valoarea primului argument.
function test(x, y = x){ 'use strict'; // eroare return x; }
Erori logice
Aceste tipuri de erori sunt cele mai greu de rezolvat. In cadrul acestui tip de eroare, nu vom avea nici-un tip de eroare prin care interpretorul JavaScript sa "ne anunte" ca ceva este in neregula. Practic scenariul de cod este executat iar rezultatul este usor diferit sau altul.
Tot ce trebuie sa faceti pentru a rezolva o eroare logica este sa va uitati din nou in cod si sa vedeti cum acesta este executat, linie cu linie.
De cele mai multe ori, erorile logice pot fi cauzate chiar de flexibilitatea cu care interpretorul JavaScript "ne lasa" sa scriem codul.
- folosirea operatorului gresit - atentie la operatorii de atribuire si operatorii de comparatie - un singur semn egal este folosit in locul a doua/trei semne egale in cadrul structurilor conditionare/repetitive
- absenta acoladelor ce delimiteaza "corpul" structurilor conditionare/repetitive - interpretorul va "adauga" doar urmatoarea linie de cod in respectiva structura
- denumirea variabilelor, constantelor, functiilor etc. nu au nici-o legatura cu ceea ce fac, ducand la confuzie pentru alti programatori
- lizibilitatea scazuta - scenariul de cod nu este indentat deloc sau corespunzator
- nu exista un standard in cadrul companiei prin care codul sa fie scris - fiecare programator va scrie codul dupa cat de pregatit este
Adauga cod JavaScript in pagina
Adaugarea de cod JavaScript intr-un document HTML se poate realiza in cateva moduri. Recomandarea este ca respectivul cod sa fie incarcat in browser dupa ce codurile HTML si CSS au fost incarcate. In acest mod putem evita generarea unor erori in codul JavaScript.
Modul extern
Adaugarea de cod JavaScript in modul extern presupune ca respectivul cod sa fie adaugat intr-un fisier separat de documentul/documentele HTML si apoi inserat in pagina web folosind eticheta HTML <script>. Aceasta eticheta va avea atributul src cu valoarea intr-un sir de caractere ce reprezinta calea relativa sau absoluta catre fisierul cu cod JavaScript.
Calea absoluta si calea relativa.
Calea absoluta
Calea absoluta reprezinta calea catre un fisier sau director intr-un sistem de operare incepand de la partitia unde fisierul/directorul se afla si continuind (daca este necesar) cu directorul sau directoarele de pana la fisierul/directorul tinta. In calea absoluta, directoarele sunt separate de caracterul special suportat de sistemul de operare. De cele mai multe ori este caracterul slash (/).
Calea absoluta a unui fisier ce se afla pe un server web incepe de la domeniul acestuia si continua pana la fisierul/directorul tinta iar in calea catre acesta pot fi zero, unul sau mai multe directoare.
Calea relativa
Calea relativa presupune parcurgerea din directorul curent pana la fisierul sau directorul tinta. In calea relativa pot fi folosite caracterul punct, caracter ce reprezinta directorul curent si doua caractere punct, unul langa altul, ce reprezinta directorul prezent cu un nivel mai sus (directorul "parinte"). Mai departe sunt trecute numele directorului sau al directoarelor pana la fisierul/directorul tinta.
Observatie: Fisierul JavaScript va avea intotdeauna extensia .js iar codul JavaScript este scris in acest fisier fara eticheta pereche HTML <script>.
<!DOCTYPE html> <html> <head> <title></title> <!-- cod CSS --> </head> <body> <!-- cod HTML --> <script src="nume-fisier.js"></script> </body> </html>
Modul intern
Modul intern de adaugare de cod JavaScript intr-un document HTML este tot prin folosirea etichetei HTML <script>. Diferenta consta in faptul ca nu vom mai avea nici-un atribut pe eticheta de deschidere iar codul JavaScript se va afla intre cele doua etichete.
Observatie: Eticheta HTML <script>este eticheta pereche.
<!DOCTYPE html> <html> <head> <title></title> <!-- cod CSS --> </head> <body> <!-- cod HTML --> <script> // cod JavaScript </script> </body> </html>
Modul pe element (eticheta HTML)
Adaugarea de cod JavaScript pe un element HTML reprezinta o alta modalitate de a insera un astfel de cod. Acest lucru se poate realiza identic ca in cazul adaugarii unui atribut HTML cu o valoare pe o eticheta HTML.
Numele atributului poate fi denumirea unui eveniment precedat de prefixul on. Cel mai intalnit eveniment este click. In interiorul ghilimelelor, reprezentand "valoarea atributului" vom avea cod JavaScript.
<!DOCTYPE html> <html> <head> <title></title> <!-- cod CSS --> </head> <body> <div class="" onclick=""></div> </body> </html>
Observatie: Adaugarea de cod JavaScript direct pe o eticheta HTML este cel mai neinspirat mod. Nu il folositi.
Comentarii in cod
Limbajul de programare JavaScript, la fel ca toate limbajele de programare, ne permit sa adaugam comentarii in codul nostru. Aceste comentarii sunt ignorate de interpretor, dar au ca rol sa ajute la intelegerea intregului cod sau a unui bloc de cod.
In JavaScript este posibil sa adaugam doua tipuri de comentarii:
- pe o singura linie
- pe mai multe linii
Comentariu pe o singura linie
Acest tip de comentariu se poate adauga in cod folosind oriunde in codul nostru JavaScript doua caractere slash (//) urmat de orice comentariu text. Comentariul pe o singura linie se termina atunci cand interpretorul intalneste caracterul invizibil de "rand nou" (\n).
<!DOCTYPE html> <html> <head> <title></title> <!-- cod CSS --> </head> <body> <!-- cod HTML --> <script> // comentariu pe o linie </script> </body> </html>
Comentariu pe mai multe linii
Acest tip de comentariu este foarte diferit de cel prezentat anterior, in sensul ca implica doua caractere speciale ce trebuie declarate in locul din script unde incepe comentariul si locul in script unde se termina comentariul.
Caracterele speciale folosite sunt slash (/) si asterisk (*). Ordinea acestora intr-un comentariu pe mai multe linii este diferita.
<!DOCTYPE html> <html> <head> <title></title> <!-- cod CSS --> </head> <body> <!-- cod HTML --> <script> /* acesta este un comentariu pe mai multe linii */ </script> </body> </html>
Unul din avantajele folosirii comentariului pe mai multe linii este acela ca putem "comenta" foarte usor si rapid un intreg bloc de cod ce nu dorim sa fie executat de interpretor atata timp cat este "comentat". In acest fel, pastram codul pentru o utilizare viitoare si nu va mai trebui sa il scriem din nou.
CONSOLA BROWSERULUI
Consola browserului reprezinta zona din browser unde diferite erori aparute sau mesaje pot fi citite. Erorile sunt generate cel mai adesea de interpretor atunci cand citeste sau executa codul JavaScript iar mesajele sunt trimise din codul JavaScript de programator.
Vizualizare consola
Cand avem browserul deschis putem vedea consola lui apasand tasta F12 sau combinatia de taste Ctrl + Shift + I ce mai reprezinta si efectuarea in browser a click dreapta de la mouse ori apasarea butonului dreapta de la touchpad. Mai apoi vom vedea un meniu cu optiuni din care vom alege optiunea Inspecteaza sau echivalent in limba in care aveti browserul.
In grafica nou aparuta (cel mai adesea in partea dreapta a ecranului) puteti efectua o singura data clic pe textul Console din meniul orizontal.
Afiseaza un mesaj in consola
Afisarea unui mesaj in consola (sau un tip de data) se face prin folosirea obiectului console. Acesta are in componenta lui mai multe metode (functii ale obiectului) insa pe noi ne intereseaza metoda log(). In interiorul parantezelor rotunde vom adauga tipul de data, mesajul sau textul pe care vrem sa il vedem in consola. Daca vrem sa afisam un mesaj text, va trebui sa il adaugam in interiorul unor ghilimele duble sau simple ori in interiorul a doua caractere backtick.
OBIECTE IMPLICITE
Obiectul window
Obiectul window reprezinta fereastra browserului si orice scenariu de cod in limbajul JavaScript va fi executat in cadrul acestui obiect. Acest obiect contine o multime de proprietati (variabile) si metode (functii) pe care le putem folosi in scenariile de cod.
Membrii ai obiectului (metode si / sau proprietatii).
Alte obiecte
JavaScript contine si alte obiecte iar o categorie aparte este reprezentata de obiectele literare. Aceste obiecte literare sunt specifice tipurilor de date.
CE ESTE SCOPUL IN JavaScript
Scopul reprezinta o caracteristica a limbajului JavaScript prin care este stabilita "vizibilitatea" variabilelor, obiectelor ori al functiilor. In JavaScript sunt doua tipuri de scopuri:
- scop global - implicit
- scop local
Scopul global
Toate variabilele declarate in afara functiilor sau declarate in interiorul functiilor fara cuvintele rezervate pentru acest rol (var let const) sunt variabile ce apartin scopului global si pot fi accesate si/sau modificate de orice linie de cod.
Durata de "viata" a variabilelor din scopul global se incheie atunci cand este inchisa fereastra browserului ori tab-ul respectiv.
Observatie: Variabilele declarate in scopul global folosind cuvintele rezervate let si const au un comportament diferit fata de cele declarate folosind cuvantul rezervat var. Daca cu cel din urma puteam declara o variabila care imediat era atribuita obiectului window, cu primele doua cuvinte rezervate acest lucru nu se intampla.
let text = 'Salut !'; console.log(text); // Salut ! console.log(window.text === text); // false const alt_text = 'Ce mai faci ?'; console.log(alt_text); // Ce mai faci ? console.log('alt_text' in window); // false
Scopul local
Functiile creaza in corpul acestora un scop local iar toate variabilele declarate in acest scop local, folosind cuvintele rezervate pentru acest rol, sunt vizibile doar in acest scop, neputand fi accesate si modificate din scopul global. Totodata, functiile pot accesa orice variabila ori constanta din scopul global.
Putem avea in cadrul unei functii, alte functii imbricate, fiecare functie putand fi astfel functie "copil" dar si functie "parinte" in acelasi timp. Regula in cadrul scopului local este ca o functie "parinte" nu poate accesa variabila/variabilele unei functii "copil", in schimb o functie "copil" va putea accesa variabilele functiei/functiilor "parinte", "bunic", "strabunic" etc. Nu uitati sa folositi cuvintele rezervate in limbaj pentru declararea unei variabile sau constante in interiorul functiilor.
Durata de "viata" a variabilelor din scopul local se incheie atunci cand intreg scenariul de cod din corpul functiei este executat.
Observatie: Daca este folosit cuvantul rezervat var pentru declararea unei variabile in cadrul unei structuri conditionare sau repetitive, variabila respectiva apartine scopului din care si respectiva structura face parte.
Observatie: Variabilele declarate in cadrul structurilor conditionare sau repetitive folosind cuvintele rezervate let si/sau const creaza un scop local in cadrul acestor structuri.
TIPURI DE DATE
Ce este o variabila
La modul cel mai simplu, o variabila nu este altceva decat un spatiu de memorie alocat pe calculator ce poate avea sau nu o valoare.
La modul mai detaliat, o variabila reprezinta un spatiu de memorie fizica pe un calculator cu o adresa in format hexazecimal (minim 1-2-4-8 octeti | 8-16-32-64 biti) si care poate fi accesata. Acest spatiu de memorie poate sa contina sau nu o valoare si cand nu mai este nevoie de acest spatiu, acesta trebuie eliberat pentru a fi folosit in alt scop.
Variabila in limbajul C.
Limbajul C este un limbaj compilat iar variabilele ocupa spatiul de memorie alocat in functie de tipul variabilei. Tipurile variabilelor sunt urmatoare:
- char
- int
- float
- double
- void
Fiecare variabila din lista mai sus mentionata, ocupa de la 1 octet (8 biti) la 4 octeti (32 biti).
Variabila in limbajul JavaScript
JavaScript este un limbaj interpretat iar acest lucru ne permite luxul de a lasa interpretorul din browser sa aloce cata memorie este nevoie, automat pentru variabilele noastre in functie de valoarea atribuita variabilelor. Tot interpretorul va elibera spatiul de memorie alocat variabilelor.
Declararea unei variabile
Declararea unei variabile in limbajul JavaScript este un proces relativ simplu si trebuie tinut cont de scopul (global sau local) unde aceasta variabila isi va duce "existenta".
In procesul de declarare al unei variabile pot fi folosite cuvintele rezervate in limbaj prezentate in lista urmatoare:
- var
- let
Observatie: Putem declara o variabila in limbajul JavaScript si fara aceste cuvinte. Procedand astfel vom face ca respectiva variabila sa apartina scopului global daca va fi declarata intr-o structura conditionata ori repetitiva sau in cadrul unei functii. Variabila (in acest ultim caz) ar fi parte din blocul de cod din aceasta functie si nu in cadrul unei functii copil (functie declarata intr-o alta functie).
Cuvantul rezervat var
O variabila declarata folosind cuvatul rezervat var in afara oricarei functii apartine scopului global si poate fi folosita de catre orice linie de cod din intreg scenariul de cod.
Pentru interpretorul de limbaj JavaScript, declararea variabilelor folosind cuvantul cheie var in corpul unei functii, presupune redeclararea variabilei la scopul local din cadrul functiei. Mai exact, o variabila declarata cu acest cuvant rezervat in corpul unei functii, este "vizibila" in intreg scopul local al acesteia chiar daca aceasta variabila este declarata exclusiv in cadrul unei structuri conditionare ori repetitive. Acest proces mai poarta denumirea si de "ridicare" (en. hoisting).
Observatie: Daca o astfel de variabila este initializata in interiorul unei structuri conditionare if, valoarea pe care aceasta o va avea inaintea structurii conditionare este nedefinita (undefined).
Cuvantul rezervat let
Acest cuvant este un alt cuvant rezervat pe care il putem folosi in declararea unei variabile. De cele mai multe ori este folosit pentru a stabili exact "vizibilitatea" in cadrul unui scop in respectivul bloc de cod.
Zona temporala moarta
Variabila declarata folosind cuvantul rezervat let si constanta declarata si (obligatoriu) initializata intr-un anume scop, fie el global ori local, sunt accesibile dupa ce acestea au fost declarate in respectivul scop.
Accesarea acestora in acelasi scop inaintea declararii duce la asa zisa "zona temporala moarta" ce nu este altceva decat locul din respectivul scop unde aceasta variabila ori constanta nu "exista".
Numele unei variabile
Fiecare variabila va avea obligatoriu un nume pentru ca mai apoi sa stim valoarea carei variabile sa o folosim in scenariul de cod. Numele variabilei trebuie sa respecte anumite conditii iar in lista urmatoare acestea sunt prezentate.
- numele variabilei va incepe doar cu litera sau semnul sublinie (en. underscore)
- numele variabilei poate contine dupa primul caracter (litera sau sublinie), alte litere (mici sau mari), unul sau mai multe caractere sublinie ori una sau mai multe cifre
- numele variabilei nu trebuie sa contina caracterul spatiu
Conflict de nume
Variabilele declarate in acelasi scop (global sau local) folosind cuvintele rezervate pentru acest rol si care au acelasi nume, vor genera aproape intotdeauna erori de sintaxa. Mai exact, daca declaram doua sau mai multe variabile cu acelasi nume folosind cuvintele rezervate var, let si/sau const sigur vom avea o eroare de sintaza. Nu acelasi lucru se intampla daca declaram doua variabile cu acelasi nume folosind cuvantul rezervat var.
Initializarea unei variabile
O variabila reprezinta un spatiu de memorie ce poate avea sau nu o valoare. Scopul pentru care o variabila este creata este de a avea/stoca o valoare. Procesul de atribuire a unei valori unei variabile poarta denumirea de initializare a variabilei. Pentru a initializa o variabila este folosit operatorul de atribuire. Acesta este reprezentat dintr-un singur semn egal.
Constante
O constanta este un alt tip de variabila. Variabila constanta are obligatoriu o valoare ce nu poate fi schimbata atata timp cat valoarea este "primitiva". O valoare primitiva este acel tip de data ce nu este un obiect sau nu contine proprietati ori metode (variabile ori functii). In lista urmatoare sunt prezentate tipurile de date primitive.
- variabila cu text
- variabila cu numar natural cu/fara fractie zecimala
- variabila bigint
- variabila booleana
- variabila cu valoarea null - desi este obiect, acesta nu contine proprietati ori metode
- variabila undefined
- variabila Symbol
In declararea unei variabile constante vom folosi cuvantul rezervat const.
Variabila cu text
Tipul de data ce are ca valoare un text sau o succesiune de zero sau mai multe caractere (litera, cifra, caracter special inclusiv caracterele "albe") prezent in interiorul unei perechi de ghilimele simple, duble ori a doua caractere backtick reprezinta o variabila cu text sau o variabila sir.
Daca in sirul de caractere vom avea un caracter folosit la delimitarea tuturor caracterelor, acesta trebuie "scapat" prin adaugarea inaintea acestuia a caracterului backslash (\). In caz contrar, vom avea generata o eroare de sintaxa.
Caractere "albe".
- " " - caracterul spatiu
- \t - caracterul tab
- \v - caracterul tab vertical
- \n - caracterul de rand nou
- \r - caracterul de "intoarcere" pe rand nou
- \f - caracterul "separator de pagina" ori "sectiune"
Caracterele ghilimele simple si duble
var numeVariabila1 = 'Text test.'; let numeVariabila2 = ''; let numeVariabila3 = "Acesta este un simplu text."; let variabilaTextCuBackslash = "... text ... \"citat\" ... text";
Siruri sablon si caracterul special backtick (→`←)
Caracterul backtick este introdus in JavaScript odata cu versiunea 6 a limbajului si poate fi folosit in declararea unei variabile text. Vom folosi doua astfel de caractere pentru ca o variabila text poate avea zero sau mai multe caractere.
Folosind doua caractere backtick putem crea diferite siruri sablon, in sensul ca in interiorul acestora putem adauga diferite expresii JavaScript.
Pentru a delimita o expresie JavaScript de restul caracterelor va trebui sa folosim caracterul special semn dolar ($) urmat de o pereche de acolade. In interiorul acoladelor putem adauga expresia JavaScript.
let limbaj = 'JavaScript'; let text = `Limbajul ${limbaj} este foarte popular.`; console.log(text); // Limbajul JavaScript este foarte popular.
Observatie: Folosirea necorespunzatoare a caracterelor albe in interiorul sirurilor sablon duce la o dimensiune inutila a sirului plus ca daca aceste caractere "ajung" mai apoi intr-o eticheta HTML (ex. <pre>) in care aceste caractere albe pot afecta dispunerea caracterelor text in pagina, efectul ar fi unul neplacut.
Variabila cu numere naturale
Numerele naturale reprezinta un alt tip de valoare ce poate fi atribuita unei variabile. Desi aria de numere naturale ce pot fi folosite este foarte mare, exista cate o limita sigura pentru valorile numerice naturale negative si o alta limita sigura pentru valorile numerice naturale pozitive. Orice valoare sub/peste aceste limite sigure pot face ca rezultatul obtinut sa nu fie cel dorit.
Obiectul literar Number contine doua constante ce reprezinta aceste valori.
let minimN = Number.MIN_SAFE_INTEGER; // let maximN = Number.MAX_SAFE_INTEGER; // let numar = 14000; console.log(numar); // 14000
Proprietatea Infinity
Aceasta proprietate apartine obiectului window si are ca valoare un substituit pentru o valoare numerica foarte mare ce nu este suportata de limbajul JavaScript.
Variabila numerica cu fractii zecimale
Practic, acest tip de variabila nu este altceva decat o variabila cu o valoare reprezentand un numar natural cu fractie. Partea fractionara este separata de partea intreaga de caracterul punct.
let numar = 14000.41; console.log(numar); // 14000.41
Variabila matrice indexabila
Variabila matrice este tipul de variabila ce poate contine zero sau mai multe valori. Aceste valori pot fi de tip text, numeric sau alte tipuri. Regula in declararea unei astfel de variabile este ca toate valorile din variabila matrice indexabila sa fie separate de operatorul virgula. Dupa ultima valoare din matrice nu mai este nevoie sa folosim nici-o virgula. Daca variabila matrice indexabila are o singura valoare, nu este nevoie de virgula.
Modalitatile prin care putem declara o variabila matrice indexabila sunt urmatoarele:
- folosind caracterele paranteze drepte [] - valorile vor sta intre parantezele drepte
- folosind obiectul literar Array() prin apelarea acestuia - valorile vor sta intre parantezele rotunde
- apeland metoda of() a obiectului literar Array - valorile vor sta intre parantezele rotunde ale metodei
let matrice1 = [14000, 41, 'text aleatoriu']; let matrice2 = Array(100, 150, 15000); let matrice3 = Array.of(800, 850, 250); console.log(matrice1); // [14000, 41, 'text aleatoriu'] console.log(matrice2); // [100, 150 ,15000] console.log(matrice3); // [800, 850, 250]
Accesarea unei valori din variabila matrice indexabila
Modalitatea prin care putem accesa o valoare dintr-o variabila matrice indexabila este sa folosim imediat dupa numele variabilei parantezele drepte iar in interiorul acestora vom adauga o valoare numerica ce reprezinta a cata valoare este in matrice.
Observatie: Numaratoarea incepe de la cifra zero. Prima valoare are indexul zero, a doua valoare are indexul unu, a treia valoare are indexul doi s.a.m.d. Totodata, nu uitati ca numarul de valori dintr-o variabila matrice indexabila poate fi aflat si numarand cate valori exista.
let matrice = [14000, 41, 'text aleatoriu']; console.log(matrice[1]); // 41
Suprascrierea unei valori dintr-o variabila matrice indexabila
Acest lucru este foarte usor de realizat si este aproape identic cu aflarea unei valori din matrice, diferenta constand ca mai apoi trebuie sa declaram o valoare pentru acel index din variabila.
let matrice = [14000, 41, 'text aleatoriu']; // afisam valoarea din matrice de la indexul 1 console.log(matrice[1]); // 41 // schimbam valoarea din // variabila de la indexul 1 matrice[1] = 42; // afisam valoarea din nou din // matrice de la indexul 1 console.log(matrice[1]); // 42
Variabila matrice indexabila multidimensionata
Spuneam mai sus ca valorile pentru o variabila matrice indexabila pot fi de tip text, numeric sau alte tipuri. Unul dintre aceste alte "tipuri" pot fi chiar alte matrice de acest tip. Practic este o variabila matrice indexabila ce contine cel putin o alta variabila matrice indexabila ca valoare intr-un anumit loc din variabila. Mai simplu spus, o variabila matrice indexabila este "parinte" pentru cel putin o alta variabila matrice indexabila "copil", care l-a randul ei poate fi "parinte" pentru o alta variabila matrice indexabila s.a.m.d.
let matrice = [1, 2, 3, [4, 5, 6], 7, 8, 9];
Accesarea si editarea unei valori dintr-o variabila matrice indexabila multidimensionata
Acest procedeu este aproape identic cu cel prin care aflam o valoare de la un anumit index din variabila matrice indexabila. Diferenta este ca daca la un anumit index avem o alta variabila matrice indexabila, v-a trebui sa folosim inca un set de paranteze drepte ce va contine indexul de la care vrem sa aflam/afisam sau sa editam valoarea respectiva.
let matrice = [1, 2, 3, [4, 5, 6], 7, 8, 9]; // afisare valoare initiala console.log(matrice[3][2]); // 6 // editam valoarea matrice[3][2] = 41; // afisare valoare noua console.log(matrice[3][2]); // 41
Destructurarea unei matrice
Destructurarea unei matrice se realizeaza folosind o pereche de paranteze patrate. In interiorul acestora vom adauga una sau mai multe nume de variabile ce vor "primii" fiecare cate o valoare dintr-o variabila matrice ce este declarata ca "valoare" pentru acestea. Trebuie sa fim atenti la ordinea variabilelor pe care le vom declara in interiorul parantezelor patrate.
let culori = ['red', 'blue', 'green']; let [r,g,b] = culori; console.log(r); // red console.log(g); // blue console.log(b); // green
Schimbarea valorilor
In JavaScript 5 schimbarea valorilor sau inversarea valorilor unor variabile se putea realiza in diferite moduri. Din JavaScript 6 acest lucru se poate realiza intr-o maniera extrem de simpla.
let r = 'red', g = 'green', b = 'blue', x = 'white', y = 'black', z = 'cyan'; [r,g,b] = [x,y,z]; console.log(r); // white console.log(g); // black console.log(b); // cyan
Parametrul trei puncte si lucrul cu destructurarea unei matrice
Parametrul trei puncte reprezinta o caracteristica a limbajului JavaScript inca din versiunea 6. Acesta trebuie sa fie urmat de un nume, nume ce reprezinta numele unei variabile. Acesta trebuie sa fie prezent o singura data si sa fie ultimul declarat.
// Exemplul 1 let culori1 = ['red', 'green', 'blue']; let [...culoriCopiate1] = culori1; console.log(culoriCopiate1); // ['red', 'green', 'blue'] // Exemplul 2 let culori2 = ['red', 'green', 'blue']; let [...culoriCopiate2, red] = culori2; // eroare (Rest element must be last element) console.log(culoriCopiate2);
JSON - JavaScript Object Notation
JSON reprezinta un tip de data obiect ce poate contine valori sub forma unei perechi de cheie cu o valoare.
Sintaxa JSON-ului presupune ca fiecare cheie poate fi adaugata intre ghilimelele duble (nefiind obligatoriu) si mai apoi trebuie separata de valoarea catre care va face referire de caracterul doua puncte. Dupa fiecare valoare este nevoie sa adaugam operatorul virgula pentru a separa perechile de chei cu valoare intre ele. Dupa ultima valoare din JSON nu este nevoie sa adaugam caracterul virgula.
let varJSON1 = {}; let varJSON2 = { "cheie1": "valoare1", "cheie2": "valoare2", cheie3: "valoare3" };
Tipuri de valori pe care o cheie le poate avea
In cadrul JSON-ului, cheile pot avea diferite valori. Aceste valori pot fi de tip sir de caractere, tip numeric, tip boolean, variabila de tip obiect reprezentat de o matrice, o functie, o variabila cu valoarea null sau chiar un alt JSON.
Accesarea valorilor din JSON
Modalitatile prin care vom putea accesa valorile dintr-un obiect JSON sunt prezentate in lista urmatoare.
- folosind operatorul de acces direct reprezentat de caracterul punct ce va fi adaugat intre numele variabilei JSON si numele cheii din JSON catre care vrem sa ii folosim valoarea
- folosind numele variabilei JSON urmat imediat de o pereche de paranteze drepte ce va contine numele cheii din JSON careia vrem sa ii folosim valoarea - cheia va fi adaugata intre parantezele drepte si mai apoi in interiorul unei perechi de ghilimele duble
let varJSON = { "cheie1": "valoare1", "cheie2": "valoare2", cheie3: [1, 2, 3] }; console.log(varJSON.cheie1); // valoare1 console.log(varJSON["cheie1"]); // valoare1 console.log(varJSON.cheie3); // [1, 2, 3]
Destructurarea unui JSON
In JavaScript 5 accesarea unei valori a unei proprietati dintr-un JSON se putea efectua foarte simplu.
var data = { nume: 'Popescu', prenume: 'Ion' }, nume = data.nume, prenume = data.prenume; console.log(nume); // Popescu console.log(prenume); // Ion
Incepand cu versiunea JavaScript 6 putem sa accesam datele dintr-un JSON mult mai rapid pentru ca incepand cu aceasta versiune avem parte de o noua sintaxa. Sintaxa noua presupune sa declaram in interiorul unei perechi de acolade una sau mai multe variabile ce au aceeasi denumire ca si cea a proprietatilor JSON-ului. Daca sunt mai mult de o variabila, acestea trebuie separate de operatorul virgula.
let data = { nume: 'Popescu', prenume: 'Ion' }; let { nume, prenume } = data; console.log(nume); // Popescu console.log(prenume); // Ion
In situatia in care nu vom atribui o valoare JSON pentru aceste variabile, interpretorul de limbaj JavaScript va returna o eroare.
let data = { nume: 'Popescu', prenume: 'Ion' }; var { nume, prenume }; // eroare let { nume, prenume }; // eroare const { nume, prenume }; // eroare
Acum sa revenim la ideea anterioara. Putem merge mai departe si sa suprascriem valorile implicite ale unor variabile folosindu-ne de parantezele rotunde.
let data = { nume: 'Popescu', prenume: 'Ion' }, nume = 'Vali', prenume = 'Valentin'; ({ nume, prenume } = data); console.log(nume); // Popescu console.log(prenume); // Ion
Retineti ca trebuie sa introduceti in parantezele rotunde intreaga instructiune de atribuire a destructurarii. Parantezele semnaleaza interpretorului de JavaScript ca ceea ce se afla in interiorul acoladelor nu este un bloc de cod, ci o expresie si ar trebui interpretata ca o expresie, permitand completarea asignarii.
Valorile implicite
Pana acum am vazut cum putem destructura un obiect JSON si sa atribuim valorile proprietatilor obiectului JSON variabilelor cu aceeasi denumire al proprietatilor acestuia insa declarate in afara obiectului JSON. Mai mult de atat am vazut in ultimul exemplu cum se poate suprascrie valoarea implicita a unor variabile declarate in afara obiectului JSON.
O siuatie la care trebuie sa fim atenti este aceea atunci cand numele proprietatii nu coincide cu numele variabilei din afara obiectului JSON sau pur si simplu nu exista. In aceasta situatie putem sa declaram o valoare implicita pentru respectiva variabila atunci cand destructuram obiectul JSON.
let data = { nume: 'Popescu', prenume: 'Ion' }, nume = 'Vali', prenume = 'Valentin'; ({ nume, prenume, functia = 'Programator' } = data); console.log(nume); // Popescu console.log(prenume); // Ion console.log(functia); // Programator
Destructurarea folosind alte nume de variabile
In aceasta situatie lucrurile se complica putin insa in sensul ca sintaxa se aseamana foarte mult cu cea a unui JSON scris in JavaScript 5.
let data = { nume: 'Popescu', prenume: 'Ion' }; let { nume: numePrincipal, prenume: numeSecundar } = data; console.log(numePrincipal); // Popescu console.log(numeSecundar); // Ion
Se observa cum interpretorul de JavaScript va cauta in JSON-ul data proprietatile nume si prenume iar valorile acestora le vor atribui variabilelor numePrincipal si numeSecundar, in aceasta ordine. Acum este timpul sa vedem si cum atribuim o valoare implicita a unei variabile in acest context.
let data = { nume: 'Popescu', prenume: 'Ion' }; let { nume: numePrincipal, prenume: numeSecundar, functia: functiePrincipala = 'Programator' } = data; console.log(numePrincipal); // Popescu console.log(numeSecundar); // Ion console.log(functiePrincipala); // Programator
Se observa ca este aplicat acelasi principiu ca si in cazul atribuirii unei valori unei variabile.
Extragerea unui JSON dintr-o variabila cu JSON-uri
let data = { nume: 'Popescu', prenume: 'Ion', functie:{ principala: 'Programator', secundara: 'Arhitect' }, adresa:{ oras:'Bucuresti' } }; let { functie, adresa } = data; console.log(functie.secundara); // Arhitect console.log(adresa.oras); // Bucuresti
Extragerea unei valori dintr-un JSON ce se afla in alte JSON-uri
let data = { nume: 'Popescu', prenume: 'Ion', functie:{ dimineata:{ principala: 'Programator' }, seara:{ secundara: 'Arhitect' } }, adresa:{ oras:'Bucuresti' } }; let { functie: { dimineata }, functie: { seara } } = data; console.log(dimineata.principala); // Programator console.log(seara.secundara); // Arhitect
Ceea ce se intampla in acest caz este ca interpretorul de JavaScript se "uita" in obiectul JSON data si "cauta" dupa proprietatea functie. Interpretorul "vede" ca acesta proprietate are ca valoare un alt JSON de JSON-uri sau macar sa contina un JSON si ca trebuie sa returneze intr-o variabila JSON-ul ce are denumirea declarata intre acolade. Totodata numele variabilei returnate este aceeasi cu cel a JSON-ului returnat.
Variabila booleana
Variabila booleana este reprezentata de o variabila ce va contine unul din urmatoarele cuvinte rezervate.
- true
- false
let bool1 = true; let bool2 = false;
Observatie: Echivalentul numeric pentru cuvantul true este reprezentat de orice valoare numerica diferita de cifra zero. Cifra zero, fie -0 sau 0, este echivalentul cuvantului rezervat false.
Variabila cu valoarea null
Cuvantul rezervat null, atribuit ca valoare unei variabile reprezinta absenta intentionata a oricarei valori.
let numeVariabila = null;
Valoarea primitiva Symbol
Odata cu versiunea 6 a limbajului JavaScript avem o noua valoare primitiva. Aceasta se numeste symbol. Aceasta noua valoare primitiva este diferita fata de celelalte in sensul ca aceasta se creaza folosindu-ne doar de functia Symbol(), functie ce poate avea si un parametru reprezentand un sir de caractere.
let sym = Symbol('text'); console.log(typeof sym); // symbol console.log(sym); // Symbol(text) let data = { [sym]: "Programator" } console.log(data[sym]); // Programator
Observatii:
- operatorul typeof a fost folosit pentru a vedea ce tip de data este operandul din dreapta lui
- nu vom putea adauga diferite valori variabilei symbol pentru ca ne este interzis sa convertim o valoare primitiva symbol la o valoare sir de caractere let sym = Symbol('uid'); console.log(sym + ' text'); // eroare
- nu vom putea instantia functia Symbol() deoarece aceasta nu are constructor let sym = new Symbol('uid'); // eroare
Partajarea simbolurilor
Veti intalni situatii in care s-ar putea sa doriti ca diferite parti ale codului dvs. sa utilizeze aceleasi simboluri. De exemplu, sa presupunem ca aveti doua tipuri diferite de obiecte in aplicatia dvs. ce ar trebui sa utilizeze aceeasi proprietate simbol pentru a reprezenta un identificator unic. Urmarirea simbolurilor in fisiere sau in baze de coduri mari poate fi dificila si predispusa la erori. De aceea, din JavaScript 6 putem avea un registru global de simboluri pe care il puteti accesa in orice moment.
Cand doriti sa creati un simbol pentru a fi partajat, utilizati metoda Symbol.for() in loc sa apelati metoda Symbol(). Metoda Symbol.for() accepta un singur parametru, care este un identificator de sir pentru simbolul pe care doriti sa-l creati. Acest parametru este utilizat si ca descriere a simbolului.
let uid = Symbol.for('uid'); let data = {}; data[uid] = "12345"; console.log(data[uid]); // 12345 console.log(uid); // Symbol(uid) let uid2 = uid; console.log(uid === uid2); // true console.log(data[uid2]); // 12345 console.log(uid2); // Symbol(uid)
Un aspect unic si foarte important este acela ca putem accesa cheia asociata simbolului. Pentru asta ne vom folosi de metoda Symbol.keyFor().
let uid = Symbol.for('uid'); console.log(Symbol.keyFor(uid)); // uid let uid2 = Symbol.for('uid'); console.log(Symbol.keyFor(uid2)); // uid let uid3 = Symbol('uid'); console.log(Symbol.keyFor(uid3)); // undefined
Proprietati ale symbolurilor
Metodele Object.keys() si Object.getOwnPropertyNames() pot prelua toate numele proprietatilor dintr-un obiect. Prima metoda returneaza toate denumirile de proprietati enumerabile, iar a doua returneaza toate proprietatile indiferent de enumerabilitate. Din ​​JavaScript 6 a fost adaugata metoda Object.getOwnPropertySymbols() pentru a va permite sa preluati simbolurile proprietati dintr-un obiect. Aceasta ne va returna o matrice cu toate proprietatile simbolului.
let uid = Symbol.for('uid'); let data = { [uid]: '12345' }; let sym = Object.getOwnPropertySymbols(data); console.log(sym.length); // 1 console.log(sym[0]); // Symbol(uid) console.log(data[sym[0]]); // 12345
Expunerea operatiunilor interne cu simboluri bine cunoscute
O tema centrala pentru JavaScript 5 a fost expunerea si definirea unora dintre partile "magice" ale limbajului JavaScript, parti pe care dezvoltatorii nu le-au putut emula in acel moment. Versiunea JavaScript 6 duce aceasta traditie mai departe expunand chiar si mai mult logica interna a limbajului. In primul rand prin utilizarea proprietatilor prototipului simbolului pentru a defini comportamentul de baza al anumitor obiecte.
JavaScript 6 are simboluri predefinite care reprezinta comportamente obisnuite in JavaScript si care au fost considerate anterior ca operatiuni interne. Fiecare simbol bine cunoscut este reprezentat de o proprietate asupra obiectului Symbol, cum ar fi Symbol.create().
- Symbol.hasInstance() - metoda folosita de instanceof pentru a determina mostenirea unui obiect
- Symbol.isConcatSpreadable - valoare booleana indicand faptul ca Array.prototype.concat() ar trebui sa alinieze elementele colectiei daca colectia este trecuta ca parametru pentru Array.prototype.concat()
- Symbol.iterator() - metoda care returneaza un iterator
- Symbol.match() - metoda folosita de String.prototype.match() pentru a compara siruri de caractere
- Symbol.replace() - metoda folosita de String.prototype.replace() pentru a inlocui substring-urile
- Symbol.search() - metoda folosita de String.prototype.search() pentru a localiza substring-urile
- Symbol.species() - constructorul pentru realizarea obiectelor derivate
- Symbol.split() - metoda folosita de String.prototype.split() pentru a imparti siruri de caractere
- Symbol.toPrimitive() - metoda care returneaza reprezentarea unei valori primitive a unui obiect
- Symbol.toStringTag - un sir folosit de Object.prototype.toString() pentru a crea o descriere a obiectului
- Symbol.unscopables - un obiect ale carui proprietati sunt numele proprietatilor obiectului care nu trebuie incluse intr-o declaratie cu cuvantul cheie with.
OPERATORII
Ce sunt operatorii
Operatorii sunt acele entitati ce ne permit sa operam diferite operatiuni pe variabile sau valori. Valorile mai poarta denumirea si de operanzi.
La nivel hardware.
La nivel hardware operatorii nu sunt altceva decat niste "porti" fizice prin care semnalele electrice intra si ies. Fiecare operator ce se regaseste intr-un limbaj de programare are un corespondent "poarta" la nivel hardware.
Operatorii in limbajul C.
Limbajul de programare compilat C ne permite sa "supraincarcam" aproape toti operatorii disponibili (in limbajul C) "oferind" altor limbaje interpretate sa "primeasca" alti operatori. In limbajul C nu putem sa declaram alti operatori.
In functie de numarul operatorilor folositi dar si al operanzilor folositi, operatorii sunt impartiti in urmatoarele categorii:
- operatori unari - foloseste un operator
- operatori binari - foloseste doi operatori identici
- operatorul ternar - foloseste trei operanzi
Impreuna, aceste categorii de operatori sunt impartiti in alte categorii:
- operatori matematici
- operatori de atribuire
- operatori unari
- operatori de comparatie
- operatori de incrementare si decrementare
- operatori logici
- operatori de lucru pe siruri de caractere
- operatori conditionari de atribuire
- operatori de tip
- operatorul de acces direct
- operatorul de instantiere
Operatori matematici
inapoi la lista cu operatorii ↑Operatorii matematici efectueaza operatiile matematice. In tabelul de mai jos sunt afisati operatorii matematici si rolul fiecaruia.
Exemplu | Nume | Rezultat | Informatii suplimentare |
---|---|---|---|
+a | identitate | converteste variabila a la numar natural pozitiv cu sau fara fractie zecimala | let a = '5'; +a; // 5 |
-a | negatie | opusul lui +a | let a = '5'; -a; // -5 |
a + b | adunare | calculeaza suma lui a + b | console.log(3 + 2); // 5 |
a - b | scadere | calculeaza diferenta dintre a - b | console.log(3 - 2); // 1 |
a * b | inmultire | calculeaza rezultatul inmultirii dintre a * b | console.log(3 * 2); // 6 |
a / b | impartire | calculeaza rezultatul impartirii intre a / b | console.log(3 / 2); // 1.5 |
a % b | modulo | returneaza partea zecimala a impartirii dintre a % b | console.log(3 % 2); // 1 |
a ** b | exponential | ridica pe a la puterea b si returneaza rezultatul |
Ordinea operatiilor. Orice operatie matematica pe care o vom efectua si la care vom folosi acest operator va trebui sa stim ca operatia matematica cu acest operator este execuata prima inainte de toate. console.log(2*3**5); // 486Restrictie la operatorul exponential.
console.log((-3)**5); // -243
let x = -3**5; // eroare
|
Operatori de atribuire
inapoi la lista cu operatorii ↑Operatorii de atribuire sunt acei operatori prin care o valoare de sine statatoare sau valoarea unei alte variabile este atribuita unei variabile.
In tabelul urmator sunt prezentati operatorii de atribuire si ce fac ei.
Exemplu | Identic | Descriere |
---|---|---|
let a = 41; | let a = 41; | Variabilei a ii este atribuita valoarea din dreapta operatorului singur semn egal. |
let a += 41; | let a = a + 41; | Valoarea variabilei a este copiata, apoi se efectuaza operatia matematica cu valoarea din dreapta operatorului de adunare iar rezultatul este adaugat intr-o noua variabila a. |
let a -= 41; | let a = a - 41; | Valoarea variabilei a este copiata, apoi se efectuaza operatia matematica cu valoarea din dreapta operatorului de scadere iar rezultatul este adaugat intr-o noua variabila a. |
let a *= 41; | let a = a * 41; | Valoarea variabilei a este copiata, apoi se efectuaza operatia matematica cu valoarea din dreapta operatorului de inmultire iar rezultatul este adaugat intr-o noua variabila a. |
let a /= 41; | let a = a / 41; | Valoarea variabilei a este copiata, apoi se efectuaza operatia matematica cu valoarea din dreapta operatorului de impartire iar rezultatul este adaugat intr-o noua variabila a. |
let a %= 41; | let a = a % 41; | Valoarea variabilei a este copiata, apoi se efectuaza operatia matematica cu valoarea din dreapta operatorului modulo iar rezultatul este adaugat intr-o noua variabila a. |
a &= b | ||
a ^= b | ||
a |= b | ||
a <<= b | ||
a >>= b | ||
a >>>= b | ||
[var_1, var_2] = [val_1, val_2] | operator de destructurare pe matrice indexabila | |
[var_1, var_2] = {var_1: val_1, var_2: val_2} | operator de destructurare pe obiectul JSON |
Operatii de atribuire neortodoxe
Operatorii de atribuire pot fi folositi intr-un mod neortodox insa perfect legal din punct de vedere al interpretorului de limbaj. Mai departe, puteti sa continuati exemplul meu schimband operatorii de atribuire cu alti operatori.
let a = (b = 41) + 5; console.log(a); // 46 console.log(b); // 41
Operatori unari
inapoi la lista cu operatorii ↑Operatorii unari folosesc de cele mai multe ori un singur operator si opereaza direct pe biti modificand informatia stocata intr-o variabila in cel mai mic detaliu.
Cum este stocata valoarea unei variabile la nivel de biti.
Fiecare caracter din alfabetul latin ce se regaseste la tastatura, fie el litera mica sau mare, orice caracter special, toate cifrele intr-un singur exemplar sau spatiile "albe" (caracter spatiu, tab etc.) si alte caractere ce nu se regasesc la tastatura, au un corespondent numeric (intre 0 si 127 sau extins pana la 255 - vezi tabelul ANSII) si ocupa un singur octet (8 biti). Acest octet este reprezentat de la dreapta la stanga sau invers (in functie de fabricantul componentei hardware) de bitul cel mai nesemnificativ si pana la bitul cel mai semnificativ. In cele mai multe cazuri avem incepand din dreapta bitul cel mai nesemnificativ (primul bit) si bitul cel mai semnificativ (al 8-lea bit).
Fiecare din acesti biti pot avea doar una din cele doua valori: 1 sau 0. Pana la al 7-lea bit (dar si dupa) se poate stoca si reprezenta in 0 si 1 o valoare numerica ce poate fi pozitiva sau negativa (intre -128 si 127). Bitul cel mai semnificativ (al 8-lea) poate fi "asignat" sau "neasignat" reprezentand, dupa caz, o valoare numerica pozitiva sau negativa al tuturor bitilor de pana la el, adica toti 7. Asadar, daca este setat la 1, valoarea in total al celor 7 biti de pana la el este negativa, iar daca este setat la 0, valoarea in total al celor 7 biti de pana la el este pozitiva.
Incepand de la bitul cel mai nesemnificativ (primul bit) si pana la al 7-lea bit (dar si dupa), valoarea fiecarui bit reprezinta o valoare dubla fata de precedentul bit.
Sa presupunem ca toti biti sunt setati la 0, inclusiv bitul cel mai semnificativ. Asadar, stocam la nivel de biti doar valori pozitive. Daca primul bit este setat la 0 (00000000), valoarea in total al bitilor este pozitiv 0 iar daca este setat 1 (00000001), valoarea in total al bitilor este pozitiv 1. Valorile bitilor de pana acum ne-au permis sa reprezentam valorile numerice 0 si 1. Sa mergem mai departe. Daca al doilea bit este setat 1, si primul bit este setat 0 (00000010), atunci avem reprezentat numeric cifra 2. Acesti biti pot fi combinati in sensul ca ambii pot fi setati la 1 si astfel reprezentam cifra 3 in cod binar (00000011). Mergand mai departe la bitul al 3-lea si il setam si pe acesta la 1 lasand restul bitilor setati la 0 (00000100). Astfel am reprezentat cifra 4 numeric in cod binar. La fel ca si in cazul prezentarii cifrei 3, putem combina acesti biti prezentati pana acum. Asfel, setam bitul 3 la 1, bitul 2 la 0 si bitul 1 la 1 (00000101). In acest mod reprezentam cifra 5 in cod binar. Daca setam bitul 3 la 1, bitul 2 la 1 si bitul 1 la 0 (00000110), avem reprezentat cifra 6 in cod binar. Daca setam bitul 3 la 1, bitul 2 la 1 si bitul 1 la 1 (00000111), avem reprezentat in cod binar cifra 7.
Se intelege pana acum ca daca un bit este setat 1, acesta reprezinta o valoare numerica dublata pana la el de la primul bit. Daca este setat 0, acel bit nu reprezinta o valoare ci doar un reper numeric dublu pentru urmatorul bit ce poate fi setat la 1.
In acest mod sunt reprezentati caracterele de la tastatura ce au fiecare cate un corespondent numeric. Spre exemplu cifra mare A are corespondentul numeric 65 iar in cod binar este reprezentata numeric pe un octet astfel: 01000001.
In cazul cifrelor reprezentarea binara se face alocand mai multi octeti decat in cazul caracterelor (caracterele, inclusiv cifrele ar fi declarate in variabila sub forma unui sir - fiecare caracter in limbaj latin ocupa un octet), respectiv 4 octeti (32 biti) pentru valorile numerice naturale si 8 octeti (64 biti) in cazul valorilor numerice cu fractie zecimala.
Este foarte usor sa calculam valoarea numerica naturala pozitiva ce poate fi suportata la nivel hardware, iar acest lucru presupune sa stim pe cati biti este sistemul de operare. Daca acesta este pe 64 de biti, atunci operatia matematica se rezuma doar la 2 la puterea 64 minus valoarea 1 (264-1 = 18446744073709552000). La nivel software se pot efectua diferite operatii prin care acest numar este mult mai mare.
Exemplu | Nume | Descriere |
---|---|---|
a & b | SI (AND) pe biti | Incepand de la dreapta la stanga, se compara bit cu bit de la fiecare variabila si daca la aceeasi pozitie ambii biti au valoarea 1, rezultatul este 1, in caz contrar rezultatul este 0. |
a | b | SAU (OR) pe biti | Incepand de la dreapta la stanga, se compara bit cu bit de la fiecare variabila si daca la aceeasi pozitie unul sau ambii biti au valoarea 1, rezultatul este 1, in caz contrar rezultatul este 0. |
a ^ b | SAU EXCLUSIV (XOR) pe biti | Incepand de la dreapta la stanga, se compara bit cu bit de la fiecare variabila si daca la aceeasi pozitie doar unul din ambii biti are valoarea 1, rezultatul este 1, in caz contrar rezultatul este 0. |
~a | Complementare/Inversare (NOT) pe biti | Incepand de la dreapta la stanga, daca un bit este setat la 1, acesta este setat la 0. Daca un bit este setat la 0, acesta este setat la 1. Practic se inlocuieste valoarea de dinainte cu cealalta valoare disponibila in codul binar, cod ce este in baza 2 (valorile 0 si 1). |
a << b | Deplasare stanga pe biti | Deplaseaza toti biti variabilei a spre stanga cu cate pozitii este specificat ca valoare in variabila b. Atentie la noua valoare pentru ca deplasarea pe biti inseamna ca fiecare pozitie inseamna inmultire la 2 a valorii respective pentru fiecare bit. |
a >> b | Deplasare dreapta pe biti | Deplaseaza toti biti variabilei a spre dreapta cu cate pozitii este specificat ca valoare in variabila b. Atentie la noua valoare pentru ca deplasarea pe biti inseamna ca fiecare pozitie inseamna impartire la 2 a valorii respective pentru fiecare bit. |
a >>> b | Deplasare dreapta pe biti neinsemnat | Deplaseaza toti biti variabilei a spre dreapta cu cate pozitii este specificat ca valoare in variabila b. Daca se depasesc biti alocati variabilei a, acestia din urma sunt "aruncati". Rezultatul fiind intotdeauna o valoare numerica pozitiva. |
delete | Sterge un "membru" dintr-un obiect. Returneaza o valoare booleana. | Folosit (cel mai adesea) pentru a sterge o proprietate dintr-un JSON sau o valoare dintr-o matrice. Indiferent daca proprietatea/valoarea exista sau nu in obiect, acest operator returneaza intotdeauna true. Daca incercam sa stergem o variabila "primitiva", acest operator va returna false. |
void | Evalueaza o expresie pentru a returna undefined. | Folosit pentru a returna cat mai rapid aceasta valoare in caz de succes sau o valoare booleana in caz contrar. void (0 == 0); // undefined | void 0!==0; // true | void 0==0; // false |
Operatori de comparatie
inapoi la lista cu operatorii ↑Operatorii de comparatie sunt folositi pentru a compara doua valori, de obicei de tip text sau numeric si care se afla sub forma unor valori in doua variabile. Aproape toti acesti operatori returneaza o valoare booleana. In tabelul urmator puteti sa vedeti acesti operatori.
Exemplu | Nume | Descriere |
---|---|---|
a == b | Egal | Acest operator compara si returneaza true daca valorile variabilelor a si b sunt egale. In caz contrar returneaza false. |
a === b | Identic | Acest operator compara si returneaza true daca valorile variabilelor a si b dar si tipul acestora sunt egale. In caz contrar returneaza false. |
a != b | Nu este egal | Acest operator compara si returneaza true daca valorile variabilelor a si b nu sunt egale. In caz contrar returneaza false. |
a !== b | Nu este identic | Acest operator compara si returneaza true daca valorile variabilelor a si b dar si tipul acestora nu sunt egale. In caz contrar returneaza false. |
a < b | Mai mic decat | Acest operator compara si returneaza true daca valoarea variabilei a este mai mica decat valoarea variabilei b. In caz contrar returneaza false. |
a > b | Mai mare decat | Acest operator compara si returneaza true daca valoarea variabilei a este mai mare decat valoarea variabilei b. In caz contrar returneaza false. |
a <= b | Mai mic decat sau egal | Acest operator compara si returneaza true daca valoarea variabilei a este mai mica sau egala decat valoarea variabilei b. In caz contrar returneaza false. |
a >= b | Mai mare decat sau egal | Acest operator compara si returneaza true daca valoarea variabilei a este mai mare sau egala decat valoarea variabilei b. In caz contrar returneaza false. |
in | Operator pentru testare (sintaxa: 'numeProprietate' in obiect) | Acest operator testeaza existenta unei proprietati din cadrul unui obiect implicit al limbajului (obiect literar: Object, Array, String etc.) sau declarat explicit de programator si returneaza true daca respectiva proprietate exista, in caz contrar returneaza false. Putem testa existenta unei valori din cadrul unei matrice. (let matrice = [1]; 0 in matrice; // true) |
Operatori de incrementare si decrementare
inapoi la lista cu operatorii ↑Acesti operatori sunt folositi la incrementarea si decrementarea in mod prefixat (imediat inaintea variabilei) si postfixat (imediat dupa numele variabilei) al variabilelor primitive de tip numeric.
Incrementarea si decrementarea se efectueaza adaugand sau scazand cu valoarea 1 din valoarea existenta a variabilei.
Sintaxa acestor operatori este foarte asemanatoarea cu cea a operatorilor matematici de adunare si scadere. Practic presupune prezenta a doua caractere de adunare in cazul operatorului de incrementare si a doua caractere de scadere in cazul operatorului de decrementare.
let m = 41; let n = 14000; console.log(m); // 41 console.log(n); // 14000 // EXEMPLU 1 // operator prefixat de incrementare ++m; console.log(m); // 42 // operator postfixat de incrementare m++; console.log(m); // 43 // EXEMPLU 2 // operator prefixat de decrementare --n; console.log(n); // 13999 // operator postfixat de decrementare n--; console.log(n); // 13998
Totodata, este foarte important cum folositi acesti operatori. Daca vrem sa afisam valoarea unei variabile dar sa o si incrementam/decrementam, valoarea afisata s-ar putea sa nu fie cea la care v-ati fi asteptat.
let m = 41; console.log(++m); // intai incrementeaza, apoi afiseaza valoarea, m are acum 42 console.log(m--); // intai afiseaza valoarea, apoi decrementeaza, m are acum 41 console.log(m); // 41
Dupa cum se poate vedea, ambii operatori pot fi folositi in mod prefixat dar si mod postfixat. Cateva reguli in intelegerea acestor operatori mai bine sunt urmatoarele:
- Forma prefixata a operatorilor ++, -- urmeaza regula "schimba valoarea" si apoi "foloseste".
- Forma postfixata a operatorilor ++, -- urmeaza regula "foloseste" si apoi "schimba valoarea".
Operatori logici
inapoi la lista cu operatorii ↑Operatorii logici sunt de tip binari (folosesc doi operatori), returneaza intotdeauna o valoare booleana si sunt folositi pentru a compara minim o valoare sau un operand. Acesti operatori sunt folositi cel mai adesea in cadrul diferitelor structuri de control conditionare.
In tabelul urmator sunt prezentati acesti operatori:
Exemplu | Nume | Descriere |
---|---|---|
!a | NOT (NEGATIE) | Compara si returneaza adevarat (true) daca valoarea variabilei a nu este adevarata. |
a && b | AND (SI logic) | Compara si returneaza adevarat (true) daca ambele valori ale variabilelor a si b sunt adevarate. |
a || b | OR (SAU logic) | Compara si returneaza adevarat (true) daca cel putin una din valorile variabilelor a sau b sunt adevarate. |
let m = true; let n = false; console.log(!m); // fals (false) console.log(m && n); // fals (false) console.log(m || n); // adevarat (true)
Operatori de lucru pe siruri de caractere
inapoi la lista cu operatorii ↑In JavaScript avem posibilitatea de a lucra cu sirurile de caractere folosind doi operatori. Acesti operatori sunt folositi si in operatiile matematice insa atunci cand sunt folositi pe siruri de caractere au denumirile de operator de concatenare (+) si operator de concatenare-atribuire (+=).
let litere = "abc"; let litere_suplimentare = litere + "def"; console.log(litere_suplimentare); // abcdef let js = "versiune "; js += "^6"; console.log(js); // versiune ^6
Diferite valori adaugate la un sir
Indiferent daca avem in stanga sau in dreapta operatorului de concatenare un operand sub forma unui sir, o valoare numerica sau de alt tip sau chiar o expresie, rezultatul este un sir.
let sir = "abc"; console.log(sir + "-1"); // abc-1 console.log(sir + 10); // abc10 console.log("abc" + !0); // abctrue console.log("abc" + null); // abcnull console.log("abc" + [1,2,3]); // abc1,2,3 1 + 2 * 3 + "abc"; // 7abc ["abc", 1, ["def", 0]] + "abc" // abc,1,def,0abc
Operatori conditionari de atribuire
inapoi la lista cu operatorii ↑Acesti operatori sunt folositi pentru a returna o valoare in functie de o anumita conditie. In tabelul urmator puteti vedea acesti operatori si ce fac.
Operator | Nume | Sintaxa |
---|---|---|
?: | Operator ternar | let variabila = expresie1 ? expresie2 : expresie3; |
?? | Operatorul contopirii null (Null coalescing) | let variabila = valoare1 ?? valoare2 ?? valoare3 ?? valoare_implicita; |
Operatorul ternar
Returneaza expresie2 daca expresie1 este adevarata. In caz contrar, expresie3 este returnata ca valoare.
let comparatie = 2 > 5 ? "adevarat" : "fals"; console.log(comparatie); // fals
Operatorul contopirii null (Null coalescing)
Returneaza prima valoare care exista si nu este nula.
let culoare = null ?? "verde" ?? "alb"; console.log(culoare); // verde
Operatori de tip
inapoi la lista cu operatorii ↑- instanceof
- typeof
Operatorul de tip instanceof
Acest operator este folosit in Programarea Orientata pe Obiecte pentru a afla daca o variabila obiect instantiaza o anumica clasa sau functie. Operatorul instanceof returneaza o valoare booleana true in caz de succes sau valoarea booleana false in caz de esec.
function numeFunctie(){} let varObiect = new numeFunctie; console.log(varObiect instanceof numeFunctie); // true class numeClasa{} let varObj = new numeClasa; console.log(varObj instanceof numeClasa); // true
Operatorul de tip typeof
Operatorul de tip typeof returneaza un sir de caractere ce reprezinta tipul de data al operandului. Operandul poate fi o variabila sau un tip de data.
typeof undefined // "undefined" typeof 41 // "number" typeof 41n // "bigint" typeof "abc" // "string" typeof typeof 1 // "string" typeof [] // "object" typeof {} // "object" typeof null // "object" typeof true // "boolean" typeof function(){} // "function" typeof class A{} // "function"
Operatorul de acces direct
inapoi la lista cu operatorii ↑Operatorul de acces direct este reprezentat de caracterul punct si este folosit pentru a accesa o variabila (proprietate) sau functie (metoda) a unui obiect.
Operatorul de instantiere
inapoi la lista cu operatorii ↑Operatorul de instantiere new este folosit in cadrul Programarii Orientate pe Obiecte. Mai exact, acesta este folosit la instantiarea unei functii sau a unei clase in limbajul JavaScript.
Observatie: Doar in JavaScript putem instantia o functie declarata cu cuvantul function.
STRUCTURI DE CONTROL LOGICE
Ce sunt structurile de control logice
Structurile de control logice reprezinta o caracteristica foarte importanta in aproape toate limbajele de programare. Acestea permit programatorilor sa execute un bloc de cod (din corpul structurii logice) daca o anumita expresie este adevarata. In cazul in care respectiva expresie nu este adevarata, acel bloc de cod va fi ignorat si nu va fi executat. Numele unei astfel de structuri de control logice este if.
In aproape toate cazurile unde este folosita o expresie pentru a fi testata si verificata daca este adevarata, sunt folositi unul sau mai multi operatori, nu neaparat la fel.
Structurile de control logice pot fi prezente oriunde in scenariul de cod iar blocul de cod ce "asteapta" sa fie executat daca expresia este catalogata ca adevarata, poate contine aproape orice. Incepand de la declararea unei variabile, continuind cu una sau mai multe structuri de control logice (imbricate) si pana la declararea sau instantierea diferitelor obiecte. Nenumarate sunt cazurile unde o astfel de structura este folosita pentru a "opri" din executie blocul de cod din care si structura logica face parte. Acest bloc de cod poate fi in cadrul unei functii sau in cadrul unei structuri de control repetitive sau in diferite variante.
Structuri de control logice cu mai multe verificari de expresii
Structura de control logica if nu este singura care poate testa daca o anumita expresie este adevarata dar este prima folosita. Putem "ordona" expresiile pentru a fi verificate si pentru fiecare expresie testata putem executa un alt bloc de cod. Binenteles, putem testa cate o expresie folosind cate if-uri dorim, declarate una sub cealalta.
O varianta mai buna ar fi folosirea "in continuarea" unei structuri if, a unei structuri else if. Aceasta poate fi folosita doar dupa o alta structura de control logica if si putem avea cate ne dorim.
Structura de control cu executia unui bloc de cod daca expresia nu este adevarata
Am vazut mai sus ca putem testa una sau mai multe expresii pentru a vedea daca sunt adevarate si sa executam un bloc de cod pentru fiecare caz.
Ce putem face daca expresia este catalogata ca falsa si vrem sa executam un alt bloc de cod in acest caz. Pentru aceasta situatie putem folosi in cazul structurii de control logice if, o structura else. Aceasta structura va contine blocul de cod ce va fi executat in cazul in care expresia este catalogata ca fiind falsa.
Structura else nu contine in nici-un limbaj de programare o modalitate prin care este verificata o expresie. Aceasta expresie va executa blocul de cod din "corpul" ei daca expresia din cadrul if-ului este catalogata ca fiind falsa.
Este important de retinut ca structura else este optionala in cadrul unei structuri if si intotdeauna va fi declarata ultima, dupa if sau ultima prezenta a setului de structuri if(){} else if(){} else if(){}.
Structura de control if(){} else if(){} else{}
Structura if
Structura de control if este alcatuita din cuvantul cheie if, urmat obligatoriu de o pereche de paranteze rotunde unde este adaugata expresia pentru a fi testata si de corpul structurii. Corpul poate contine blocul de cod ce va fi executat daca expresia este catalogata ca fiind adevarata. Corpul este delimitat (cel mai adesea) de o singura pereche de acolade. Daca blocul de cod ce reprezinta corpul structurii este alcatuit doar dintr-o singura linie de cod, perechea de acolade poate fi omisa.
let variabila_numerica = 41; if(variabila_numerica < 14000){ console.log("Valoarea este mai mica."); }
Structura else if
Structura else if ofera posibilitatea de a testa cel putin inca o expresie dupa structura if. Asadar, putem avea una sau mai multe structurii else if. Regula de scriere pentru else if este aproape identica ca si in cazul structurii if doar ca vom folosi doua cuvinte cheie else if. Intotdeauna vom adauga aceasta structura dupa structura if.
let variabila_numerica = 41; if(variabila_numerica > 14000){ console.log("Valoarea este mai mica."); } else if(variabila_numerica == 41){ console.log("Valorile sunt egale."); }
Structura else
Aceasta structura este optionala si daca este folosita va fi declarata ultima. Intotdeauna va fi declarata dupa o structura de control logica if sau dupa ultima structura de control logica else if. Prezenta acestei structuri ofera posibilitatea ca va fi executat un bloc de cod daca expresia testata in cadrul if-ului sau al tuturor expresiilor testate in cadrul else if-urilor de dinainte a returnat fals (false).
let variabila_numerica = 41; if(variabila_numerica > 14000){ console.log("Valoarea este mai mica."); } else if(variabila_numerica == 41){ console.log("Valorile sunt egale."); } else{ console.log("Valoarea este mai mica."); }
Structura switch
Aparent, structura de control logica switch este foarte asemanatoare ca regula de scriere cu cea a unei structuri if. Diferenta consta (evident) in prezenta cuvantului cheie switch in locul if-ului dar si a altor cuvinte rezervate ce pot fi folosite in cadrul acestei structuri. Asadar, vom avea ca regula de scriere dupa folosirea cuvantului cheie switch o pereche de paranteze rotunde si o pereche de acolade.
Structura switch reprezinta o varianta la setul de testare al expresiilor if(){} else if(){} else if(){} else{}.
Pentru a testa mai multe expresii in cadrul unei structuri switch vom avea nevoie de cuvintele cheie case, default dar si de break.
Cu ajutorul cuvantului cheie case vom testa fiecare valoare ce ar fi returnata din expresie sau in urmatorul exemplu daca o variabila are o anumita valoare.
Folosirea cuvantului cheie break este optionala insa este recomandata pentru ca odata ce expresia a returnat adevarat si s-a executat blocul de cod de dupa cuvantul cheie case, si restul de blocuri de cod din celelalte case-uri ramase vor fi executate.
Folosirea cuvantului cheie default este echivalenta else-ului din cadrul unei structurii if(){} else if(){} else if(){} else{}. In acest mod ne asiguram ca un bloc de cod va fi executat daca nici-un case nu a putut sa potriveasca valoarea lui cu cea din expresie.
let variabila_numerica = 41; switch(variabila_numerica){ case 40: console.log("Valoarea este 40."); break; case 41: console.log("Valoarea este 41."); break; case 14000: console.log("Valoarea este 14000."); break; default: console.log("Valoarea nu este cea ceruta."); }
STRUCTURI DE CONTROL REPETITIVE
Ce sunt structurile repetitive
In cel mai simplu mod de exprimare, structurile repetitive sunt structuri ce executa un bloc de cod din "corpul" structurii atata timp cat cel putin o conditie este adevarata.
Structurile repetitive sunt o alta caracteristica fundamentala in aproape toate limbajele de programare. Numarul acestora difera in functie de limbaj, insa sunt cateva care se regasesc aproape in orice limbaj de programare. Acestea sunt for si while / do while si au sintaxa aproape identica, diferenta fiind in modul de scriere in limbajul respectiv al operanzilor.
Reguli de baza in declararea structurilor repetitive
Modalitatea de scriere a unei astfel de structuri implica folosirea cuvantului cheie al carei tip de structura vom face referire (for si while / do while), urmat imediat de conditia sau setul de conditii/declaratii. Acestea sunt adaugate obligatoriu intre parantezele rotunde urmat imediat de "corpul" structurii ce va contine blocul de cod ce va fi executat in cadrul a zero sau nenumarate iteratii. "Corpul" structurii va fi adaugat (cel mai adesea) intr-o pereche de acolade. Daca "corpul" structurii repetitive are blocul de cod reprezentat dintr-o singura linie de cod, aceste acolade pot fi omise.
Folosirea structurilor repetitive prezentate anterior implica si diferite cuvinte cheie disponibile in tot atatea limbaje de programare unde se regasesc si aceste structuri. Aceste cuvinte cheie sunt continue si break. Fiecare din aceste cuvinte au cate un scop bine definit.
Cuvantul cheie continue este folosit in blocul de cod din "corpul" structurii, mai exact in cadrul unei structuri conditionare. In functie de zona din blocul de cod unde aceasta structura conditionara (if) se afla, "restul" blocului de cod nu va mai fi executat la acea iteratie.
Rolul cuvantului cheie break este de a "opri" executia repetitiva a blocului de cod din care face parte. De cele mai multe ori, acesta se va regasi folosit in cadrul unei structuri conditionare (posibil if) ce este parte a blocului de cod din "corpul" structurii repetitive.
Alte structuri repetitive
O structura repetitiva ce are o sintaxa diferita in functie de limbaj este foreach. Modul de scriere al acestei structuri este diferit daca vom folosi limbajele JavaScript sau PHP.
Structura repetitiva while do-while
Structura repetitiva while
Structura while este cea mai simpla structura de control repetitiva. Aceasta are o sintaxa foarte asemanatoare cu cea a structurei if. Diferenta este ca aceasta structura va executa de nenumarate ori blocul de cod din "corpul" structurii pana ce expresia este catalogata ca fiind falsa. Totodata, daca expresia este falsa de la inceput, blocul de cod din "corpul" structurii nu va fi executat.
Se intelege ca structura de control repetitiva while este alcatuita, dupa cuvantul cheie while de perechea de paranteze rotunde unde este adaugata expresia ce urmeaza a fi testata si imediat apoi de "corpul" structurii alcatuit dintr-o pereche de acolade.
let n = 1; while(n <= 10){ console.log(n++); } // 12345678910
Trebuie mentionat ca daca nu avem o modalitate prin care sa "iesim" din structura de control repetitiva while, aceasta va executa codul din "corpul" structurii de nenumarate ori. Practic vom avea o structura repetitiva ce va rula la infinit.
Pentru a "iesi" din structura while, ne vom folosi de cuvantul cheie break. Acesta va fi folosit (cel mai adesea) in cadrul unei structuri conditionare ce face parte din blocul de cod din "corpul" structurii while.
Modalitatea cea mai buna pentru a fi sigur ca nu executam nici-o linie de cod suplimentara din "corpul" structurii while, este sa adaugam "iesirea" exact la inceputul blocului de cod din corpul structurii while. Este o buna practica printre programatori.
let n = 1; while(n){ if(n > 10){ break; } console.log(n++); } // 12345678910
Structura de control repetitiva while si instructiunea do
Desi este cea mai simpla structura repetitiva, while poate fi usor complicata, cel putin la inceput.
Instructiunea do este "atasata" structurii while cu specificatia ca daca pana acum aveam declarat "corpul" structurii dupa setul de paranteze rotunde unde expresia este testata, acum vom avea "corpul" structurii dupa instructiunea do. Impreuna, acestea vor fi adaugate inaintea declaratiei structurii while. Nu uitati de caracterul punct si virgula ce trebuie adaugat dupa perechea de paranteze rotunde, locul unde expresia este testata.
let n = 0; do{ console.log(n++); } while(n <= 10); // 012345678910
Daca ar fi sa traducem intr-un limbaj uman, exemplul anterior s-ar traduce astfel: "Executa blocul de cod o data, apoi testeaza expresia prima data si repeta executarea blocului de cod atata timp cat expresia este adevarata.
Structura repetitiva for
Structura de control repetitiva for este cea mai complexa dintre toate aceste structurii. Sintaxa acesteia este un pic diferita, in sensul ca in interiorul parantezelor rotunde nu vom avea o singura expresie, ci trei, toate separate de caracterul punct si virgula.
Putem omite declararea expresiilor si astfel vom avea o structura de control infinita, pentru ca expresia a doua (si cea mai importanta) este evaluata implicit ca adevarata. Totusi nu putem omite prezenta caracterelor punct si virgula pentru ca vom avea generata o eroare de sintaxa.
"Iesirea" dintr-o structura repetitiva infinita se poate realiza folosind in blocul de cod din "corpul" structurii, o structura conditionara (if) ce va avea o expresie ce va fi evaluata ca adevarata (la o anumita iteratie a structurii for) iar in "corpul" acesteia vom avea cuvantul cheie break, cuvant ce va facilita iesirea din structura repetitiva.
Prima si a treia expresie sunt de evaluare iar a doua expresie este cea care trebuie sa fie evaluata ca adevarata. Expresia a doua poate fi evaluata ca adevarata si daca folosim orice valoare diferita de valorile primitive null si 0, insa nu este recomandat.
In cadrul acestor trei expresii putem avea mai multe declaratii, separate prin operatorul virgula. Rolul expresiilor nu se schimba insa trebuie tinut cont ca la a doua expresie, ultima declaratie din aceasta trebuie sa fie evaluata ca adevarat pentru ca blocul de cod din "corpul" structurii sa fie executat.
for(let i = 1; i <= 10; ++i){ console.log(i); } // 12345678910 let i = 1; for(;;){ if(i > 10){ break; } console.log(i++); } // 12345678910 for(let i = 1, j = 2; i <= 10, j < 5; ++i, ++j){ console.log(i); } // 123
FUNCTII
Ce sunt functiile
Functiile reprezinta unul dintre caracteristicile fundamentale in orice limbaj de programare. Acestea pot fi de sine statatoare sau pot fi parte dintr-un obiect. In ambele cazuri functiile sunt extrem de utile pentru ca au urmatoarele caracteristici:
- functiile ofera o anumita abstractizare - putem apela o functie si sa "asteptam" un rezultat anume fara sa cunoastem implementarea
- functiile ofera o mai buna logica in cod
- functiile nu se executa/apeleaza decat explicit la apelul in cod al programatorului
- functiile usureaza munca programatorului prin reutilizarea codului - o functie poate fi apelata in mai multe locuri din cod
- functiile ofera o mai buna modularitate in cod
- "corpul" unei functii este un scop local - variabilele declarate aici nu pot fi "accesate" din afara functiei
- functiile pot avea argumente - acestea pot fi folositi in "corpul" functiei si pot avea valori implicite
- functiile pot fi recursive
Functiile sunt de doua feluri:
- functii implicite ale limbajului sau declarate in librarii
- functii declarate de programator
Declararea unei functii
Declararea unei functii se poate realiza respectand un set de reguli. Aceste reguli reprezinta sintaxa de declarare a unei functii.
- o functie incepe cel mai adesea cu cuvantul cheie function
- dupa cuvantul cheie function urmeaza numele functiei
- numele functiei va incepe doar cu caracterul sublinie sau litera mica ori mare
- intre numele functiei si cuvantul cheie function trebuie sa existe un caracter spatiu
- dupa numele functiei urmeaza o pereche de paranteze rotunde ce reprezinta locul unde pot fi adaugate diferite argumente ale functiei - daca vor fi declarati mai mult de un singur argument, acestia vor fi delimitati prin operatorul virgula
- dupa perechea de paranteze rotunde urmeaza "corpul" functiei, acesta este delimitat printr-o pereche de acolade
function numeFunctie(argument1, argument2, argumentN){ // scenariul de cod // din corpul functiei }
Apelarea unei functii
Apelarea unei functii presupune scrierea numelui unei functii/variabile urmat de o pereche de paranteze rotunde intr-o zona a scenariului de cod.
Observatii:
- Functiile in JavaScript pot fi apelate inainte de a fi declarate.
- Nu putem folosi operatori pe variabile declarate cu cuvintele rezervate let | const ce au ca valoare o functie inainte de declararea acestor variabile.
Variabila ce are ca valoare o functie
O variabila poate avea ca valoare o functie si in acest caz numele functiei poate fi omis pentru ca nu vom putea apela o functie dupa acest nume ci dupa numele variabilei.
let calc = function calculeaza(){} calc(); calculeaza(); // eroare
Functii anonime
Functiile anonime sunt functiile ce nu au un nume. Acestea pot fi intalnite cel mai adesea in urmatoarele situatii:
- ca valoare a unei variabile
- in programarea asincron, functia anonima este executata dupa ce un eveniment asteptat a avut loc
Elemente returnate de o functie
Functiile pot returna o singura valoare sau un tip de date sub forma unei variabile ori o alta functie. Acest lucru este posibil folosind cuvantul rezervat return.
Cuvantul rezervat return
Acest cuvant poate fi folosit doar in "corpul" unei functii, in orice loc din acesta. In momentul cand interpretorul de limbaj JavaScript intalneste acest cuvant, functia returneaza valoarea/codul de dupa acest cuvant ignorand orice alt cod existent pe alta linie din corpul functiei.
Functie returnata de o functie
Functiile pot returna o alta functie declarata in interiorul acesteia. Cum o functie poate fi ca valoare pentru o variabila, variabila respectiva va avea ca valoare functia returnata de functie, urmand ca mai apoi sa "apelam" variabila respectiva.
function test(){ let nume = "Valentin"; return function(){ console.log("Salut " + nume); } } let a = test(); a(); // Salut Valentin
O alta modalitate de a executa functia returnata (functie "copil") este sa apelam direct functia "parinte" cu doua perechi de paranteze rotunde, una langa cealalta.
function test(){ let nume = "Valentin"; return function(){ console.log("Salut " + nume); } } test()(); // Salut Valentin
Sintaxa noua pentru un JSON returnat
// JavaScript 5 function angajat(nume, prenume){ return { nume: nume, prenume: prenume, numeIntreg: function(){ return this.nume + ' ' + this.prenume; } } } var data = angajat('Popescu', 'Ion'); console.log(data.numeIntreg()); // Popescu Ion // Incepand cu JavaScript 6 function angajat(nume, prenume){ return { nume, prenume, numeIntreg(){ return this.nume + ' ' + this.prenume; } } } let data = angajat('Popescu', 'Ion'); console.log(data.numeIntreg()); // Popescu Ion
Argumente ale functiei
Functiile pot prelua date din afara acestora pentru a le prelucra si a returna un rezultat. Aceste date sunt "transmise" unei functii sub forma unor "argumente" in interiorul parantezelor rotunde. La "apelul" functiei, "argumentele" mai au denumirea si de "parametri".
Declararea unei functii cu argumente
function numeFunctie(a, b){ return a + b; }
Argumente cu valori implicite
Valorile implicite pentru argumentele unei functii se realizeaza exact in locul unde argumentele sunt declarate. Acest lucru se realizeaza folosind operatorul de atribuire si apoi valoarea implicita pentru acel argument.
function calc(a = 0, b = 0){ return a + b; } calc(); // 0 calc(1); // 1 calc(41, 14000); // 14041
Valorile implicite pentru argumente pot fi de orice tip de valoare ne dorim pentru ca din JavaScript 6 doar modalitatea de atribuire s-a schimbat si nu ar trebui sa ne surprinda o functie care are unul sau mai multe argumente cu valori implicite declarati in alta parte a scenariului de cod. Trebuie acordata o atentie sporita atunci cand avem un argument cu valoarea implicita reprezentat de o functie declarata in alta parte a scenariului de cod, rezultatele pot fi neasteptate.
O atentie sporita trebuie sa o aveti in momentele cand un argument este necesar pentru ca nu are o valoare implicita. Acea functie va returna NaN.
function inmultire(a, b = 0){ return a * b; } inmultire(); // NaN
Un alt aspect important este reprezentat de situatia in care un parametru este declarat ca undefined dar are un corespondent argument cu o valoare implicita. Practic se intampla acelasi lucru ca in situatia in care nu declaram valoarea pentru acel argument insa diferenta este ca ii vom spune interpretorului sa foloseasca valoare implicita pentru acel argument iar restul valorilor corespund pentru ceilalti parametri.
function inmultire(a = 2, b = 5){ return a * b; } inmultire(undefined, 3); // 6
Daca nu am fi folosit cuvantul cheie undefined interpretorul de limbaj JavaScript ne returna o eroare iar aceasta reprezenta o eroare de sintaxa.
O alta situatie este atunci cand vom declara valoarea null pentru un parametru. Sunt diferite comportamente insa pe cele mai multe va trebui sa le descoperiti singuri insa folosind exemplul anterior vom avea urmatorul rezultat.
function inmultire(a = 2, b = 5){ return a * b; } inmultire(null, 3); // 0
Valorile implicite si obiectul arguments
Un obiect implicit al limbajului este obiectul arguments. Acest obiect reprezinta o matrice indexabila cu toate argumentele functiei. Comportamentul obiectului arguments este unul diferit atunci cand sunt prezente valori implicite ale argumentelor. In JavaScript 5 modul nonstrict urmatorul exemplu ne returneaza adevarat de fiecare data.
function numeFuncCuArg(a,b){ console.log(a === arguments[0]); console.log(b === arguments[1]); a = 'trei'; b = 'patru'; console.log(a === arguments[0]); console.log(b === arguments[1]); } numeFuncCuArg('unu', 'doi'); // true // true // true // true
In modul strict acest comportament se schimba.
function numeFuncCuArg(a,b){ 'use strict'; console.log(a === arguments[0]); console.log(b === arguments[1]); a = 'trei'; b = 'patru'; console.log(a === arguments[0]); console.log(b === arguments[1]); } numeFuncCuArg('unu', 'doi'); // true // true // false // false
In ECMAScript 6 obiectul arguments se comporta ca si cum functia este in modul strict chiar daca nu este declarat aceasta optiune in interiorul functiei.
function numeFuncCuArg(a = '', b = ''){ console.log(a === arguments[0]); console.log(b === arguments[1]); a = 'trei'; b = 'patru'; console.log(a === arguments[0]); console.log(b === arguments[1]); } numeFuncCuArg('unu', 'doi'); // true // true // false // false
Un aspect important la functiile cu parametri ce au valori implicite este acela ca desi sunt declarate valori implicite pentru parametri, lungimea sau numarul tuturor parametrilor este reprezentata de numarul valorilor declarate pentru fiecare argument in parte si nu a-l valorilor implicite ale acestora.
function numeFuncCuArg(a = '',b = ''){ console.log(a === arguments[0]); console.log(b === arguments[1]); a = 'trei'; b = 'patru'; console.log(a === arguments[0]); console.log(b === arguments[1]); console.log(arguments.length); } numeFuncCuArg('unu', 'doi'); // true // true // false // false // 2 numeFuncCuArg('unu'); // true // false // false // false // 1
Observatie: Parametri functiilor au propriul lor domeniu de aplicare si este separat de corpul functiilor. Aceasta inseamna ca valoarea implicita a unui parametru nu poate accesa niciunul dintre variabilele declarate in interiorul corpului functiei.
Argumentul rest sau trei puncte
Odata cu versiunea 6 a limbajului JavaScript, s-a dorit o imbunatatire in cazul obiectului arguments. Acest lucru presupune adaugarea in limbaj a argumentului cu trei puncte (...), argument urmat cel mai adesea de un nume simbolic ce va face referire la o matrice ce va contine restul de parametri pentru respectiva functie.
function numeFuncCuArg(...args){ for(let i = 0; i < args.length; ++i){ console.log(args[i]); } } numeFuncCuArg(1,2,3,4,5); // 1 pana la 5
Argumentul trei puncte va contine restul de parametri pentru respectiva functie. Acum sa revedem functia anterioara cu singura modificare ce reprezinta inca un parametru declarat primul in cadrul functiei.
function numeFuncCuArg(a, ...args){ for(let i = 0; i < args.length; ++i){ console.log(args[i]); } } numeFuncCuArg(1,2,3,4,5); // 2 pana la 5
Restrictii asupra argumentului trei puncte
- argumentul trei puncte trebuie sa fie singurul si ultimul in cadrul acelei functii
- argumentul trei puncte nu poate fi folosit in cadrul unei functii ce are inainte cuvantul cheie set (en. setter)
Aceasta restrictie exista deoarece obiectul literar set are o restrictie de un singur parametru pe cand argumentul trei puncte nu este restrictionat de un anumit numar de parametri.
Caracteristici noi pentru obiectul literar Function
Odata cu versiunea 6 a limbajului JavaScript, obiectului literar Function i s-au atribuit noi caracteristici prin care vom scrie mai putine linii de cod.
Corpul functiei prezent ca argument
let calc = new Function('a', 'b', 'return a * b'); calc(3, 5); // 15
Se poate observa cu ochiul liber cat de simplu este sa declaram o functie in acest mod. Trebuie retinut ca fiecare parametru, dar si "corpul" functiei trebuie pus intre ghilimelele simple, duble sau intre doua caractere backtick.
Proprietatea name
Tot cu versiunea 6 a limbajului JavaScript putem afla numele unei functii foarte usor. Proprietatea name este valabila pe toate functiile si va avea ca valoare numele functiei de la care proprietatea name este apelata.
Cum declararea unei functii in JavaScript se poate realiza in mai multe moduri, trebuie retinut ca exista cateva reguli dupa care aceasta proprietate functioneaza. In exemplele urmatoare putem vedea cum aceasta proprietate este folosita pentru a afla numele functiei.
function calc(){} console.log(calc.name); // calc let inmulteste = function(){} console.log(inmulteste.name); // inmulteste let adauga = function adaugaCeva(){} console.log(adauga.name); // adaugaCeva console.log((Function()).name); // anonymous console.log((new Function()).name); // anonymous
Functii declarate si apelate in structura conditionara if
Desi acest lucru nu reprezinta o buna practica, uneori ne intalnim cu astfel de situatii, cel putin de la versiunea 6 a limbajului JavaScript. In JavaScript 5 declararea unei functii si apelarea ei in interiorul structurii conditionare if reprezenta o eroare de sintaxa.
Important la aceasta abordare este ca functia este accesibila din JavaScript 6 doar in interiorul structurii conditionare cu conditia sa executam acel bloc de cod in modul strict. In acest fel functia respectiva nu exista in afara structurii conditionare.
'use strict'; if(true){ console.log(typeof calc); // function function calc(){}; calc(); } console.log(typeof calc); // undefined
Acelasi bloc de cod executat fara modul strict face ca functia sa devina accesibila in afara structurii conditionare. Acest lucru se intampla deoarece se aplica principiul de "ridicare" (en. hoisting) la scopul din care structura conditionara face parte. Este exact acelasi procedeu aplicat si in cazul variabilelor declarate folosind cuvantul cheie var.
if(true){ console.log(typeof calc); // function function calc(){}; calc(); } console.log(typeof calc); // function
Dupa cum stim, functiile se pot declara si in alte moduri. Acum vom declara o functie anonima pe o variabila folosindu-ne de cuvantul cheie let. Intuiti ce se intampla ? Daca nu ati intuit, atunci va spun ca se aplica principiul zonei temporale moarte.
if(true){ console.log(typeof calc); // eroare let calc = function(){}; calc(); } console.log(typeof calc);
Functia "sageata"
O functie "sageata" poate fi declarata respectand cateva reguli de sintaxa:
- cuvantul cheie function nu este folosit
- daca functia sageata nu are nici-un argument, parantezele rotunde nu pot fi omise
- daca functia sageata are un singur argument, parantezele rotunde pot fi omise
- daca argumentele functiei sageata sunt mai multi de unu, acestia trebuie adaugati in parantezele rotunde si separati de operatorul virgula
- acoladele pot fi omise daca nu sunt mai mult de o singura linie de cod
- intre parantezele rotunde si acolade vom adauga caracterele egal si semnul mai mare decat reprezentand o sageata cu directia spre dreapta
Observatie: Caracterele ce formeaza o "sageata" pot tine loc de cuvantul cheie return daca acesta nu este declarat explicit in corpul functiei.
let calc = (a,b) => { return a + b; } calc(5,6); // 11 let inmulteste = (a,b) => a * b; inmulteste(3,5); // 15 let adauga = a => a; adauga('text'); // text
Absente in cadrul functiei sageata
Functiile sageata se comporta diferit fata de restul functiilor si asta pentru ca rolul lor este de a fi foarte simple si sa returneze o valoare cat mai rapid cu putinta. Mai mult decat atat, pe acestea nu putem folosi o serie de cuvinte cheie prin care sa facem o multime de lucruri foarte utile.
- this, arguments, super, new.target
- functiile sageata nu pot fi instantiate folosind cuvantul cheie new pentru ca functiile sageata nu au constructor
- nu putem extinde o functie sageata folosind cuvantul cheie prototype pentru ca acestea nu au constructor (pentru functiile sageata nu exista prototype)
Functia generator
Modul de accesare al unor informatii dintr-o matrice a facut sa apara nevoia unei eficiente crescute si chiar a unei opriri a iteratiei la un anumit moment dar si continuarea iteratiei dupa ultimul moment unde iteratia a fost oprita. In acest mod se puteau consuma mai putine resurse de pe calculatorul utilizatorului iar scenariul de cod era mai rapid executat. Mai mult decat atat, se dorea cunoasterea printr-o valoare booleana daca iteratorul a ajuns la finalul matricei.
Toate acestea se puteau realiza in JavaScript 5 prin folosirea unei functii care ne returna un obiect JSON ce contine metoda cu denumirea generic next(). Aceasta functie accesa fiecare valoare din matricea prezenta ca parametru al functiei. La fiecare apelare a functiei next(), aceasta ne returna un alt JSON cu proprietatile value si done.
Proprietatea value continea valoarea returnata iar proprietatea done contine o valoare booleana prin care ne anunta daca iteratia a ajuns la final sau nu.
function generator(array){ var i = 0; return { next: function(){ var done = (i >= array.length), value = !done ? array[i++] : undefined; return { value: value, done: done } } } } var iterator = generator(['x','y','z']); console.log(iterator.next()); // { value: x, done: false } console.log(iterator.next()); // { value: y, done: false } console.log(iterator.next()); // { value: z, done: false } console.log(iterator.next()); // { value: undefined, done: true }
Odata cu versiunea 6 a limbajului JavaScript, acest lucru se realizeaza foarte asemanator insa cu o sintaxa noua. Aceasta sintaxa noua presupune folosirea caracterului special steluta (→ * ←) intre cuvantul cheie function si numele functiei respective (nu conteaza unde, insa este de preferat sa nu existe spatiu intre acest caracter si numele functiei) ori inainte de parantezele rotunde daca numele functiei nu exista (nu in cadrul unei functii sageata). In acest mod functia respectiva este recunoscuta de interpretorul de limbaj JavaScript ca fiind o functie generator ce returneaza un obiect Generator.
Cuvantul rezervat yield
Acest cuvant este introdus in limbaj odata cu versiunea JavaScript 6 si este special creat pentru a fi folosit in corpul functiei generator, in scopul local al acesteia si nu in cadrul unei functii "copil" ce ar putea exista in corpul functiei generator.
Cuvantul rezervat yield poate fi folosit ori de cate ori se doreste sa se returneze o valoare la o anumita apelare a metodei next(), functie ce poate primi si un parametru valoare.
function *generator(array){ let i = 0; yield array[i++]; yield array[i++]; yield array[i++]; } let iterator = generator(['x','y','z']); console.log(iterator.next()); // { value: x, done: false } console.log(iterator.next()); // { value: y, done: false } console.log(iterator.next()); // { value: z, done: false } console.log(iterator.next()); // { value: undefined, done: true }
Cuvantul rezervat yield si caracterul steluta
Folosirea caracterului steluta pe cuvantul rezervat yield face ca in acest cuvant cheie sa returneze un alt obiect Generator. Trebuie avut grija ca acest cuvant sa aiba valoarea din dreapta lui o functie generator.
function *generator1(){ yield 2; yield 3; yield 4; } function *generator2(){ yield 1; yield* generator1(); } let iterator = generator2(); console.log(iterator.next()); // { value: 1, done: false } console.log(iterator.next()); // { value: 2, done: false } console.log(iterator.next()); // { value: 3, done: false } console.log(iterator.next()); // { value: 4, done: false } console.log(iterator.next()); // { value: undefined, done: true }
Observatie: Cuvantul cheie yield "returneaza" un tip de data asa cum acesta este declarat. Daca este specificat o matrice, aceasta este atribuita ca valoare proprietatii value.
Cuvantul rezervat return folosit intr-o functie generator
Functia generator este la baza o functie si din acest motiv putem merge mai departe si sa facem ca functia sa returneze o valoare diferita atunci cand a ajuns la final.
function *generator(array){ let i = 0; yield array[i++]; yield array[i++]; yield array[i++]; return 100; } let iterator = generator(['x','y','z']); console.log(iterator.next()); // { value: x, done: false } console.log(iterator.next()); // { value: y, done: false } console.log(iterator.next()); // { value: z, done: false } console.log(iterator.next()); // { value: 100, done: true }
Iteratori (metode) de colectare
Odata cu versiunea JavaScript 6 putem avea trei tipuri de obiecte de colectare: matrice si obiectele create cu ajutorul functiilor Set() si Map(). Toate acestea au urmatoarele iteratoare incorporate pentru a va ajuta sa navigati in continutul lor.
- entries() - returneaza un iterator ale carui valori sunt o pereche cheie-valoare
- values() - returneaza un iterator ale carui valori sunt valorile colectiei
- keys() - returneaza un iterator ale carui valori sunt cheile continute in colectie
Iteratorul entries()
Iteratorul entries() returneaza o matrice de doua elemente de fiecare data cand este apelata. Matricea cu doua elemente reprezinta cheia si valoarea pentru fiecare element din colectie. Pentru matrice, primul element este indicele numeric, pentru obiectele create folosind functia Set() primul element este valoarea, al doilea este cheia si ambele au aceeasi valoare. Pentru obiectele create folosind functia Map() primul element este cheia.
let data1 = [1,2,3]; let data2 = new Set([7,8,9]); let data3 = new Map(); data3.set('nume', 'vali'); data3.set('prenume', 'valentin'); for(let i of data1.entries()){ console.log(i); } // [0,1] // [1,2] // [2,3] for(let i of data2.entries()){ console.log(i); } // [7,7] // [8,8] // [9,9] for(let i of data3.entries()){ console.log(i); } // ['nume', 'vali'] // ['prenume', 'valentin']
Iteratorul values()
Iteratorul values() returneaza doar valoarea din obiectul respectiv.
let data1 = [1,2,3]; let data2 = new Set([7,8,9]); let data3 = new Map(); data3.set('nume', 'vali'); data3.set('prenume', 'valentin'); for(let i of data1.values()){ console.log(i); // 1 2 3 } for(let i of data2.values()){ console.log(i); // 7 8 9 } for(let i of data3.values()){ console.log(i); } // vali // valentin
Iteratorul keys()
Iteratorul keys() returneaza doar cheia din obiectele ce contin date. Pentru matrice returneaza doar valoarea numerica reprezentand numarul iteratiei, pentru obiectele declarate folosind functia Set() returneaza cheia care este aceeasi cu valoarea iar in cazul obiectelor create folosind functia Map() va returna cheia de acces pentru valorile din obiect.
let data1 = [1,2,3]; let data2 = new Set([7,8,9]); let data3 = new Map(); data3.set('nume', 'vali'); data3.set('prenume', 'valentin'); for(let i of data1.keys()){ console.log(i); // 0 1 2 } for(let i of data2.keys()){ console.log(i); // 7 8 9 } for(let i of data3.keys()){ console.log(i); } // nume // prenume
Iteratori initiali
Fiecare dintre aceste obiecte au iteratori initiali prin care se pot accesa datele din interiorul acestora. Iteratorul (metoda) values() este initial pentru obiectele matrice si cele create folosind functia Set() iar pentru obiectele folosind functia Map() iteratorul initial este entries(). Retineti ca acesti iteratori sunt accesibili folosind sintaxa for of.
Functiile si siruri sablon
O functie poate fi apelata fara operatorul de apelare (reprezentat dintr-o pereche de paranteze rotunde) trecand numele acesteia inaintea unui sir sablon (sirul sablon reprezinta un sir de caractere ce se afla in interiorul a doua caractere backtick ce poate contine zero, una sau mai multe expresii JavaScript, expresii prezente in interiorul a doua acolade precedat de caracterul semn dolar).
Apelarea unei functii in acest mod face ca sirul sablon sa fie atribuit ca prim parametru al functiei sub forma unei matrice cu siruri de text.
function testeazaText(caractere_text){ console.log(caractere_text); } testeazaText`Sir de text`; // ["Sir de text"]
Expresii JavaScript in interiorul unui sir sablon atribuit ca parametru unei functii
Daca in interiorul unui sir sablon avem unul sau mai multe expresii JavaScript, rezultatul din fiecare expresie este atribuit ca parametru pentru functia ce este "apelata" astfel.
Ordinea expresiilor din interiorul sirului sablon, reprezinta si ordinea in care functia primeste parametrii dupa ce a primit primul parametru reprezentat dintr-o matrice cu fiecare set de caractere pana la respectiva expresie.
let nume = "Vali", prenume = "Valentin"; function numeComplet(caractere_text, nume, prenume){ console.log(caractere_text); console.log(`${nume} ${prenume}`); } numeComplet`Numele meu este ${nume} iar prenumele este ${prenume}.`; // ["Numele meu este ", " iar prenumele este ","."] // Vali Valentin
ALERTE
Obiectul window contine trei functii prin care putem sa cream o alerta de notificare, o alerta de confirmare sau o alerta prin care putem prelua o valoare de la utilizator.
Functia alert
Functia alert poate fi folosita pentru a notifica un utilizator, aratandu-i un text (atribuit ca parametru al functiei) in fereastra implicita de dialog a browserului. Aceasta fereastra va avea un un buton de inchidere prin care utilizatorul poate inchide fereastra.
alert("Alerta cu functia alert.");
Functia confirm
Functia confirm este folosita pentru a crea o alerta de confirmare cu un text pentru utilizator. Textul este atribuit functiei sub forma de parametru. Functia confirm va returna o valoare booleana daca utilizatorul a apasat pe unul din cele doua butoane afisate in fereastra de dialog.
Observatie: Daca utilizatorul apasa tasta Esc dupa ce fereastra de dialog a aparut, aceasta fereastra va dispare iar functia va returna valoarea booleana false.
confirm("Alerta cu functia confirm.");
Functia prompt
Functia prompt este folosita pentru a crea o alerta cu un text pentru utilizator si a prelua o valoare de la acesta. Functia va putea avea zero, unul sau doi parametrii, ambii sunt optionali. Primul parametru reprezinta textul pentru utilizator iar al doilea reprezinta o valoare ce va fi adaugata de browser in campul de inserare a valorii din fereastra de dialog.
prompt("Alerta cu functia prompt.");
EXCEPTII
JavaScript try, catch, finally
O caracteristica pe care JavaScript o are si care se regaseste si in alte limbaje de programare este posibilitatea de a "incerca" (en. try) executarea unui bloc de cod ce ar putea genera o eroare in anumite situatii si "prinderea" acestei erori.
JavaScript try, catch
Blocul de cod ce ar trebui "incercat" va sta intr-o structura try si daca va genera o eroare, aceasta va fi "prinsa" intr-o structura catch. Sintaxa acestor structuri este urmatoarea.
try{ // bloc de cod ce ar // putea genera o eroare } catch(){ // bloc de cod pentru // gestionarea erorii }
Observatie: Avantajul folosiri structurilor try, catch este ca indiferent de natura erori, codul de dupa aceste structuri va fi executat.
JavaScript finally
JavaScript ofera o structura ce ne permite sa executam un bloc de cod dupa ce s-a "incercat" executia altuia si care a "aruncat" o eroare, eroare ce a fost gestionata.
Acest bloc de cod va fi executat intotdeauna dupa structurile try si catch si va sta intr-o structura finally.
try{ // bloc de cod ce ar // putea genera o eroare } catch(){ // bloc de cod pentru // gestionarea erorii } finally{ // bloc de cod ce va fi executat // dupa try si catch }
Atunci cand folosim aceste structuri trebuie sa fim atenti la urmatoarele:
- o structura try nu poate exista fara o structura catch
- structurile try, catch pot fi imbricate
- structura finally este optionala
- codul din structura finally va fi executat dupa catch si inaintea liniilor de cod de dupa aceasta structura
- daca vom avea un try, catch si finally in cadrul unei functii si daca o eroare este generata in try iar in catch si finally avem cate o declaratie return, cea din cadrul finally-ului va fi executata
JavaScript throw
Cuvantul rezervat throw este folosit pentru a "arunca" o exceptie. Modalitatile prin care putem "arunca" o exceptie in JavaScript folosind acest cuvant sunt urmatoarele:
- adaugand o expresie ce reprezinta natura erorii dupa acest cuvant
- instantiind una din functiile implicite ale limbajului dupa acest cuvant - functia va "primii" ca parametru natura erorii in format (cel mai adesea) sir de caractere
function ariaDreptunghiului(latime, inaltime){ if(!latime || !inaltime){ throw "Eroare: Unul dintre parametri nu este declarat."; } if(isNaN(latime) || isNaN(inaltime)){ throw new Error("Unul dintre parametri nu este numeric."); } } try{ ariaDreptunghiului(5); } catch(e){ console.error(e); // Eroare: Unul dintre parametri nu este declarat. } try{ ariaDreptunghiului(5, "A"); } catch(e){ console.error(e); // Error: Unul dintre parametri nu este numeric. }
PROGRAMAREA ORIENTATA PE OBIECTE - POO
Introducere in POO
Programarea procedurala reprezinta modalitatea prin care putem scrie "procedurii" (functii) ce opereaza asupra datelor (variabilelor). Mai intai sunt declarate datele si apoi sunt declarate procedurile ce vor actiona asupra datelor.
Programarea orientata pe obiecte reprezinta posibilitatea de a crea cu ajutorul claselor, sabloane ale diferitelor tipuri de obiecte ce contin atat date cat si procedurii. Datele (variabilele) in cadrul obiectelor mai au denumirea si de proprietati iar procedurile (functiile) mai au denumirea si de metode.
Programarea procedurala si programarea orientata pe obiecte
Pentru implementarea unei caracteristici noi in cadrul unei aplicatii web folosind programarea procedurala mai intai trebuie sa declaram datele pe care le prelucram si apoi sa declaram procedurile ce vor actiona asupra datelor. Codul scris astfel este pas cu pas si nu putem "sari" unul. Practic declaram fiecare variabila in parte si apoi functie dupa functie.
Pentru implementarea unei caracteristici noi in cadrul unei aplicatii web folosind programarea orientata pe obiecte putem declara obiecte ce contin atat datele cat si procedurile urmand sa "folosim" obiectul pentru a implementa caracteristica noua a aplicatiei web. Avantajele programarii orientate pe obiecte sunt urmatoarele:
- o mai clara structura a codului, fiind astfel mai usor de gestionat ori depanat probleme ce pot aparea - implementat mai usor diferite caracteristici noi in aplicatie
- scenariul de cod este executat mai rapid, ocupand mai putina memorie
- nu ne mai repetam in cod
- reduce complexitatea codului
- posibilitatea reutilizarii scenariului de cod cu obiecte in alte aplicatii
Atunci cand un obiect este creat (proces realizat prin instantierea unei clase), acesta va avea toate proprietatile si metodele declarate in interiorul clasei. Clasa fiind un sablon la ce proprietatii si metode ar trebui sa aiba un obiect.
Cuvantul rezervat new
Cuvantul rezervat new reprezinta un operator ce este folosit in procesul de instantiere al unei clase definite de utilizator. In JavaScript mai poate fi folosit si la instantierea unei functii declarate de utilizator sau la instantierea unui obiect literar (Boolean(), Number(), Function(), Array() etc.) al limbajului.
Observatie: Atunci cand instantiem o clasa/functie intr-o variabila, aceasta variabila devine un obiect cu membrii declarati in cadrul clasei/functiei.
Cuvantul rezervat this
In Programarea Orientata pe Obiecte, cuvantul rezervat this va putea fi folosit in interiorul clasei (si functiei si obiectului JSON in limbajul JavaScript) si va face referire la obiectul insusi care instantiaza clasa.
Cu ajutorul cuvantului rezervat this vom putea "accesa" in interiorul clasei, variabile (proprietati) si functii (metode) declarate in interiorul acesteia.
POO folosind functii
Programarea Orientata pe Obiecte pana la versiunea 6 a limbajului JavaScript (ECMAScript 2015) se putea realiza instantiind o functie ca "valoare" a unei variabile. Respectiva variabila devenea un obiect ce continea variabile (proprietati) si functii (metode) ale respectivei functii instantiate.
function Persoana(nume, prenume){ this.nume = nume; this.prenume = prenume; this.numeComplet = function(){ return this.nume + ' ' + this.prenume; } } let obiect = new Persoana("vali", "valentin"); console.log(obiect.nume); // vali console.log(obiect.prenume); // valentin console.log(obiect.numeComplet()); // vali valentin
POO folosind clase
Incepand cu versiunea 6 a limbajului, putem sa folosim clasele in scenariul de cod.
Declararea unei clase
Declararea unei clase se realizeaza folosind cuvantul rezervat class urmat de numele clasei si apoi corpul clasei ce este delimitat obligatoriu de o pereche de acolade. In corpul clasei vom avea proprietatile si metodele aferente clasei.
Intre numele clasei si cuvantul rezervat class trebuie sa existe obligatoriu un caracter spatiu. Numele clasei trebuie sa respecte aceleasi reguli ca si la declararea unei variabile.
Atunci cand declaram o clasa, este bine sa urmam niste reguli, scopul fiind acela al lizibilitatii codului. Regulile cele mai intalnite sunt:
- numele clasei trebuie sa fie semantic si sa inceapa cu litera mare
- numele unei proprietati si/sau al unei metode din cadrul clasei nu trebuie sa fie identic cu al numelui clasei
- numele proprietatilor si metodelor clasei trebuie sa aiba denumirea semantica si sa contina doar litere mici
Membrii clasei
Membrii clasei reprezinta setul de proprietatii si metode declarate in interiorul acoladelor clasei, ce delimiteaza "corpul" clasei.
class Limbaj{ // proprietatii // metode }
Functia constructor
Functia constructor reprezinta o metoda implicita ce este folosita pentru instantierea si crearea obiectului. Aceasta metoda poate avea argumente ce vor reprezenta parametrii atunci cand clasa este instantiata.
Observatie: Nu putem avea mai mult de o metoda constructor in interiorul unei clase.
Instantierea unei clase
Procesul de instantiere al unei clase este identic cu cel al unei functii.
class Persoana{ constructor(nume, prenume){ this.nume = nume; this.prenume = prenume; } numeComplet(){ return this.nume + ' ' + this.prenume; } } let obiect = new Persoana('vali', 'valentin'); console.log(obiect.nume); // vali console.log(obiect.prenume); // valentin console.log(obiect.numeComplet()); // vali valentin
Observatie: In JavaScript, clasele sunt un alt tip de functii, denumite si functii "speciale". typeof class NumeClasa{} // "function"
Interzicerea instantierii unei clase
Acest proces se poate realiza in constructorul clasei, acesta fiind apelat imediat ce clasa tinta este instantiata.
In cadrul constructorului clasei vom avea o structura conditionara prin care vom testa daca metaproprietatea target de pe operatorul new are aceeasi valoare cu clasa insusi. In acest proces este folosit si operatorul de comparare dublu semn egal. In situatia in care acest operator returneaza adevarat (true), vom "arunca" o eroare sub forma unui sir de caractere folosind cuvantul rezervat throw.
class Adresa{ constructor(){ if(new.target == Adresa){ throw 'Instantiere clasa Adresa'; } } } let adresa = new Adresa; // Instantiere clasa Adresa
Proprietatii si metode statice
Proprietatii si metode statice pot fi create cu ajutorul cuvantului rezervat static. Folosirea acestui cuvant se face doar declarandu-l inaintea declararii unei proprietati sau a unei metodei. Proprietatile si metodele statice au urmatoarele caracteristicii:
- fiecare proprietate declarata ca fiind statica apartine clasei si nu obiectului
- proprietatile statice sunt accesibile metodelor statice si non-statice
class Persoana{ constructor(nume, prenume){ this.nume = nume; this.prenume = prenume; } numeComplet(){ return this.nume + ' ' + this.prenume; } static nume_secundar = 'vali_2'; static prenume_secundar = 'valentin_2'; static afiseazaNume(){ return Persoana.nume_secundar + ' ' + Persoana.prenume_secundar; } } let obiect = new Persoana('vali', 'valentin'); console.log(obiect.nume); // vali console.log(obiect.prenume); // valentin console.log(obiect.numeComplet()); // vali valentin console.log(obiect.nume_secundar); // undefined console.log(obiect.prenume_secundar); // undefined console.log(Persoana.nume_secundar); // vali_2 console.log(Persoana.prenume_secundar); // valentin_2 console.log(Persoana.afiseazaNume()); // vali_2 valentin_2
Clase anonime
Numele unei clase poate fi omis insa in acest caz trebuie respectat urmatoarele reguli:
- pentru a fi folosita, clasa fara nume trebuie declarata ca valoare pentru o variabila
- daca vom avea un nume la clasa ce este ca valoare pentru o variabila, acest nume este ignorat de interpretor si nu va putea fi folosit pentru a instantia aceasta clasa intr-o alta variabila
-
operatorul de instantiere new poate fi omis, in acest caz vom accesa proprietatile si metodele dupa numele variabilei obiect
- typeof new class{}; // "object"
- typeof class{}; // "function"
- nu putem omite cuvantul rezervat class atunci cand declaram o clasa anonima
Nume de metode declarate in afara clasei
Putem declara numele unei metode in alta parte, intr-o variabila, si apoi sa o declaram in interiorul clasei unde avem nevoie.
Acest lucru se realizeaza putin diferit si presupune declararea numelui variabilei (ce contine sirul de caractere cu numele ulterior al metodei) in interiorul parantezelor drepte urmat de parantezele rotunde si apoi acoladele cu blocul de cod reprezentand corpul metodei.
let numeMetoda = 'numeIntreg'; let Angajat = class{ constructor(nume, prenume){ this.nume = nume; this.prenume = prenume; } [numeMetoda](){ return this.nume + ' ' + this.prenume; } } let persoana = new Angajat('Vali', 'Valentin'); console.log(persoana.numeIntreg()); // Vali Valentin
Observatie: Acest procedeu poarta denumirea de nume calculate (en. computed name).
Mostenirea in POO
Clasele pot mosteni alte clase. O clasa ce mosteneste o alta clasa mai are denumirea si de clasa "copil". Clasa mostenita este astfel clasa "parinte" pentru clasa ce o mosteneste. Acest concept sta la baza principiului de mostenire. Prin mostenire o clasa poate mosteni proprietati si metode din clasa "parinte".
Cuvantul rezervat super
Metoda constructor va putea apela constructorul clasei "parinte" folosind acest cuvant cheie in corpul acesteia.
Reguli ce trebuie respectate in folosirea cuvantului rezervat super:
- trebuie sa fie folosit pentru a "accesa" proprietati si metode din clasa / obiectul "parinte", "bunic" etc. - este preferat in locul cuvantului rezervat this pentru ca nu creeaza confuzie, acesta din urma este folosit (cel mai adesea) pentru a accesa membrii clasei actuale
- daca clasa "parinte" are un constructor, acesta trebuie apelat in constructorul clasei "copil" folosind cuvantul rezervat super
- cuvantul rezervat super trebuie folosit inaintea cuvantului rezervat this
Cuvantul rezervat extends
Acest cuvant este folosit in declararea unei clase ce mosteneste o alta clasa. Mai exact este folosit dupa numele clasei ce este declarata urmat de numele clasei ce va fi mostenita.
class Personal{ constructor(nume, prenume){ this.nume = nume; this.prenume = prenume; } numeComplet(){ return this.nume + ' ' + this.prenume; } } class Angajat extends Personal{ constructor(nume, prenume){ super(nume, prenume); } } let obiect = new Angajat('vali', 'valentin'); console.log(obiect.nume); // vali console.log(obiect.prenume); // valentin console.log(obiect.numeComplet()); // vali valentin
Mostenirea folosind prototype
Prototype reprezinta fundatia pentru mostenire in JavaScript si incepand cu versiunea 6 a limbajului, prototype primeste noi caracteristici.
In mod normal prototipul unui obiect este specificat atunci cand obiectul este creat, fie printr-un constructor fie folosind metoda Object.create(). JavaScript 5 ne ofera o metoda prin care putem afla prototipul unui obiect iar aceasta este Object.getPrototypeOf(). Totusi, nu putem schimba in ECMAScript 5 prototipul unui obiect odata ce acesta a fost instantiat.
Din acest motiv, odata cu versiunea 6 a limbajului JavaScript avem la dispozitie metoda Object.setPrototypeOf(). Cu ajutorul acestei metode vom putea sa schimbam prototipul unui obiect in orice moment. Aceasta metoda accepta doi parametri:
- obiectul al carui prototip ar trebui sa se schimbe
- obiectul care ar trebui sa devina prototipul primului argument
let person = { getGreeting() { return 'Hello'; } }; let dog = { getGreeting() { return 'Woof'; } }; // prototype is person let friend = Object.create(person); console.log(friend.getGreeting()); // Hello console.log(Object.getPrototypeOf(friend) === person); // true // set prototype to dog Object.setPrototypeOf(friend, dog); console.log(friend.getGreeting()); // Woof console.log(Object.getPrototypeOf(friend) === dog); // true
Folosirea cuvantului rezervat super in mostenirea cu prototype
let person = { getGreeting(){ return 'Salut'; } }; let friend = { getGreeting(){ return super.getGreeting() + ", ce mai faci ?"; } }; Object.setPrototypeOf(friend, person); console.log(friend.getGreeting()); // Salut, ce mai faci ?
Diferente intre POO cu functii si POO cu clase
- folosind functiile in POO putem declara functia ce urmeaza a fi instantiata dupa instantierea acesteia pentru ca interpretorul de limbaj o declara din nou inainte de a executa scenariul de cod inainte de instantierea acesteia in linia de cod
- folosind clasele in POO nu putem instantia o clasa inainte ca aceasta sa fie declarata - interpretorul de limbaj va genera o eroare
Obiecte literare
Obiectele literare sunt functii implicite ale limbajului pe tipurile de date. Majoritatea dintre acestea au o functie constructor si pot fi instantiate. Aproape toate obiectele literare au in cadrul lor functii (metode) si variabile (proprietati) ce pot fi folosite in diferite actiuni in scenariul de cod.
Observatie: Pentru a vedea rapid membrii obiectului literar din mai multe nivele, folositi exemplul urmator.
// in primul nivel console.log(Object.getOwnPropertyNames(numeObiectLiterar)); // in al doilea nivel sunt membrii obiectului adaugati prin mostenire console.log(Object.getOwnPropertyNames(numeObiectLiterar.prototype));
Obiectul literar String
Obiectul literar String poate fi folosit pe variabile ce contin siruri de caractere sau direct pe o valoare de acest tip.
Membrii ai obiectului (metode si / sau proprietatii).
Obiectul literar Number
Obiectul literar Number poate fi folosit pe variabilele ce contin numere naturale sigure (-2 53-1 si 2 53-1).
Membrii ai obiectului (metode si / sau proprietatii).
Obiectul literar BigInt
Obiectul literar BigInt poate fi folosit pe valori numerice in si din afara intervalului oferit de obiectul literar Number.
Membrii ai obiectului (metode si / sau proprietatii).
Obiectul literar Array
Obiectul literar Array poate fi folosit pe tipul de data matrice.
Membrii ai obiectului (metode si / sau proprietatii).
// of let arr = Array.of(1,2,3,4); // find console.log(arr.find(n => n > 3)); // 4 // findIndex console.log(arr.findIndex(n => n > 3)); // 3 // copyWithin arr.copyWithin(2,0); console.log(arr); // [1,2,1,2] // fill arr.fill(5); console.log(arr); // [5,5,5,5] // includes poate avea doi parametri: // - primul se refera la valoarea cautata // - optional al doilea de la al catelea element din matrice sa inceapa cautarea, valoarea initiala este 0 [NaN].includes(NaN); // true [-1, -0, +0, 1].includes(-0); // true [-1, -0, +0, 1].includes(+0); // true [1,2,3,4,5].includes(0); // false [1,2,3,4,5].includes(3); // true [1,2,3,4,5].includes(3,3); // false
Obiectele literare Set si Map
Obiectul literar Set() a fost creat pentru o mai buna organizare a datelor fata de o matrice iar obiectul literar Map() pentru o mai buna organizare a datelor fata de obiectul JSON.
Mediul de lucru in ECMAScript 5
In mediul de lucru in ECMAScript 5 aveam la dispozitie doar o singura modalitate prin care puteam adauga date intr-un obiect bazat in baza pozitiei acestuia in obiect.
Desi puteam folosi si un JSON, acest lucru nu era recomandat si asta pentru ca desi ne puteam baza pe o valoare numerica in cea mai mare parte, aceasta abordare nu doar ca nu era prea eficienta dar nu era nici foarte buna. Mai mult decat atat puteam avea si valori ce puteau fi accesate altfel insa ce era mai important este legat de absenta proprietatii length, proprietate prin care aflam "lungimea" obiectului.
var data = {}, k = {}; data[0] = 'text initial'; data[1] = 'text 1'; data[2] = 'text 2'; data[3] = 'text 3'; data[k] = 'text 4'; for(var i = 0; i < 5; i++){ console.log(data[i]); } // text initial // text 1 // text 2 // text 3 // undefined
Se observa ca la ultima iteratie a buclei for, aceasta ne returneaza undefined si este normal asa pentru ca bucla for cauta in baza unei valori numerice convertita in spate de interpretor la valoare sir.
Stim ca respectivul obiect contine o valoare sir prin care vom putea accesa si ultima valoare din JSON insa probabil va intrebati cum de poate fi sir in conditiile in care variabila k este tot un JSON. Ceea ce se intampla este ca acest JSON este adus la valoarea sir de catre interpretor folosind metoda toString() iar ultima valoare din JSON va putea fi accesibila dupa sirul "[object Object]"
var data = {}, k = {}; data[0] = 'text initial'; data[1] = 'text 1'; data[2] = 'text 2'; data[3] = 'text 3'; data[k] = 'text 4'; for(var j in data){ console.log(typeof j); } // 5 * string console.log(data["[object Object]"]); // text 4
Obiectul literar Set
Acest obiect ne ofera posibilitatea sa cream un obiect prin care sa depozitam diferite date in mod unicat. Interesant la acest obiect este ca putem adauga o valoare sub forma de valoare numerica dar si sub forma de sir. Singura exceptie sunt reprezentate de valorile +0 si -0 care sunt interpretate la fel.
Pentru a crea obiectul respectiv mai intai trebuie sa instantiem functia Set() iar apoi sa folosim metoda add() pentru a adauga diferite date.
let data = new Set(); data.add(1); data.add('1'); console.log(data.size); // 2
Accesarea datelor din acest obiect se va face cel mai eficient folosind metoda forEach(), metoda ce apartine obiectului. Aceasta va avea ca parametru o functie anonima cu un singur parametru ce va reprezenta valoarea din obiect.
let data = new Set(); data.add(1); data.add('5'); data.forEach((valoare) => { console.log(valoare); }); // 1 // 5
Metoda forEach() mai poate fi folosita si in cadrul obiectelor matrice dar al celor create cu ajutorul functiei Map(). Metoda forEach() poate avea pana la trei parametri iar acestia sunt:
- valoarea
- cheia
- obiectul
In cadrul obiectului creat cu ajutorul functiei Set() avem si alte metode cu denumire semantica prin care vom putea testa daca o valoare exista, sa stergem respectiva valoare ori sa stergem toate valorile din obiect.
let data = new Set(); data.add('text 1'); data.add('text 2'); data.add('text 3'); console.log(data.size); // 3 console.log(data.has('text 1')); // true data.delete('text 1'); console.log(data.size); // 2 console.log(data.has('text 1')); // false data.clear(); console.log(data.size); // 0
Membrii ai obiectului (metode si / sau proprietatii).
Obiectul literar Map
Functia Map() functioneaza foarte asemanator cu functia Set(). Mai mult, impart si cateva metode cu acelasi nume iar acestea sunt has(), delete() si clear().
Obiectele create cu ajutorul functiei Map() contin perechi de cuvinte cheie cu valoare unde cheia poate fi orice tip de data. Similar cu seturile, cheile nu sunt fortate sa determine echivalenta, ceea ce inseamna ca puteti avea o cheie numerica 3 si un sir '3' ca doua chei separate.
Adaugarea unor valori in obiectul creat cu ajutorul functiei Map() se poate face folosind metoda set() si se pot recupera folosind metoda get(). De asemenea avem la dispozitie metoda forEach() prin care vom putea itera obiectul dupa valori.
let data = new Map(); data.set('nume', 'Vali'); data.set('prenume', 'Valentin'); console.log(data.get('nume')); // Vali console.log(data.get('prenume')); // Valentin data.forEach((value, key, data) => { console.log(key + ' : ' + value); }); // nume : Vali // prenume : Valentin
Membrii ai obiectului (metode si / sau proprietatii).
Obiectul literar Boolean
Obiectul literar Boolean poate fi folosit pe tipul de data boolean.
Membrii ai obiectului (metode si / sau proprietatii).
Obiectul literar Object
Obiectul literar Object poate fi folosit pe variabilele ce contin obiecte sau functii.
Membrii ai obiectului (metode si / sau proprietatii).
// assign let defaultData = {}; let dataNow = { nume: 'Doutzen', prenume: 'Kroes', functia: 'Model VS' }; Object.assign(defaultData, dataNow); console.log(defaultData.nume); // Doutzen console.log(defaultData.prenume); // Kroes console.log(defaultData.functia); // Model VS let data = {}; Object.assign(data, { nume: 'Vali', prenume: 'Valentin', functia: 'Programator' }); console.log(data.nume); // Vali console.log(data.prenume); // Valentin console.log(data.functia); // Programator // getOwnPropertyNames let obj = { a: 1, b: { b1: 10, b2: ()=>{} }, c: ()=>{} }; Object.getOwnPropertyNames(obj); // ["a", "b", "c"] // is console.log(+0 == -0); // true console.log(+0 === -0); // true console.log(Object.is(+0, -0)); // false console.log(NaN == NaN); // false console.log(NaN === NaN); // false console.log(Object.is(NaN, NaN)); // true console.log(5 == 5); // true console.log(5 == "5"); // true console.log(5 === 5); // true console.log(5 === "5"); // false console.log(Object.is(5, 5)); // true console.log(Object.is(5, "5")); // false
Obiectul literar Function
Obiectul literar Function reprezinta functia constructor pentru toate functiile.
Membrii ai obiectului (metode si / sau proprietatii).
Obiectul literar RegExp
Obiectul literar RegExp poate fi folosit in expresiile regulate aplicate asupra sirurilor de caractere.
Membrii ai obiectului (metode si / sau proprietatii).
Obiectul literar Date
Obiectul literar Date poate fi folosit in lucrul cu data / timp.
Membrii ai obiectului (metode si / sau proprietatii).
Obiectul literar Math
Obiectul literar Math poate fi folosit in operatiile matematice.
EVENIMENTE
Ce sunt evenimentele in JavaScript
Evenimentele sunt acele actiuni ce pot fi declansate de utilizatorul paginii web atunci cand acesta foloseste pagina web (actiuni pe elementele grafice) prin diferite componente hardware. Totodata, multe alte evenimente sunt declansate de browser fara vreo actiune a utilizatorului.
Browserul reprezinta legatura cu sistemul de operare iar acesta din urma este cel care "instiinteaza" browserul de diferite "actiuni" ale componentelor harware (miscare mouse, clic stanga/dreapta de la mouse, apasare tasta de la tastatura, pe dispozitivele mobile - detectarea cu ajutorul unui senzor din interiorul dispozitivului (senzor accerelometru) a orientarii dispozitivului mobil etc.). Aceste "actiuni" reprezinta doar o parte din evenimentele declansate in browser.
Fiecare eveniment are o denumire specifica reprezentata prin unul sau mai multe cuvinte impreuna. In cea mai mare parte, cuvantul ce reprezinta un anumit eveniment este scris cu litere mici.
Evenimente sincron si evenimente asincron
Evenimente sincron
Evenimentele sincron sunt in cea mai mare parte evenimente declansate in browser iar acestea pot aparea in urmatoarele situatii:
- odata ce documentul HTML a fost incarcat complet in browser (evenimentul load)
- cand s-a efectuat clic in interiorul sau in afara etichetei / etichetelor HTML input sau textarea (evenimentele focus si blur)
- atunci cand se efectueaza dubluclic pe un element grafic (eticheta HTML)
- cand este apasata o tasta (evenimentul keydown), este tinut apasat incontinuu pe o tasta (evenimentul keypress) sau o tasta este eliberata (luat degetul de pe aceasta - evenimentul keyup)
Evenimente asincron
In lista urmatoare sunt cateva exemple unde evenimentele asincron sunt "asteptate" sa apara.
- atunci cand un element grafic sau intreg documentul HTML are bara de scroll iar continutul acestuia este derulat (evenimentul scroll)
- atunci cand calea (relativa sau absoluta) catre o resursa (imagine, fisier JavaScript ori XML) este incorecta (evenimentul error)
Liste cu evenimente in JavaScript
Evenimente declansate la incercarea incarcarii unui fisier (JavaScript / imagine / XML)
- error - acest eveniment este declansat atunci cand un fisier (JavaScript / imagine / XML) nu este gasit la calea (relativa sau absoluta) catre acesta sau respectivul fisier nu poate fi utilizat.
- abort
- load
- beforeunload
- unload
Evenimente declansate pe etichetele HTML input, textarea sau orice eticheta ce are atributul boolean contenteditable
- focus
- blur
Evenimente declansate la o conexiune WebSocket
- open
- message
- error
- close
Evenimente declansate de browser in actiunea acestuia de salvare a istoricului navigarii
- pagehide
- pageshow
- popstate
Evenimente declansate atunci cand o animatie din limbajul CSS este executata
- animationstart
- animationend
- animationiteration
Evenimente declansate pe o "tranzitie" CSS
- transitionend
Evenimente declansate intr-un formular HTML
- reset
- submit
Evenimente declansate la printarea documentului din browser
- beforeprint
- afterprint
Evenimente pe fereastra browserului
- fullscreenchange
- fullscreenerror
- resize
- scroll
Evenimente la copierea unui text
- cut
- copy
- paste
Evenimente de la tastatura
- keydown
- keypress
- keyup
Evenimente de la mouse
- auxclick
- click - declansat cand se efectueaza clic stanga de la mouse pe o eticheta HTML (element din DOM sau obiect literar - window)
- contextmenu - declansat cand se efectueaza clic dreapta de la mouse pe o eticheta HTML (element din DOM sau obiect literar - window)
- dblclick
- mousedown
- mouseenter
- mouseleave
- mousemove
- mouseover
- mouseout
- mouseup
- select
- wheel
Evenimente de la mouse prin mutarea unui element din pagina intr-o alta zona a paginii
- drag
- dragend
- dragenter
- dragstart
- dragleave
- dragover
- drop
Evenimente pe fisiere media
- canplay
- canplaythrough
- durationchange
- emptied
- ended
- loadeddata
- loadedmetadata
- pause
- play
- playing
- ratechange
- seeked
- seeking
- stalled
- suspend
- timeupdate
- volumechange
- waiting
Evenimente declansate la incarcarea unei resurse
- abort
- error
- load
- loadend
- loadstart
- progress
- timeout
- change
- storage
Eveniment declansat la actualizarea unei informatii locale din browser
- storage
Evenimente declansate la schimbarea unor valori
- hashchange - declansat in obiectul window daca proprietatea hash din obiectul location a fost schimbata (location.hash = "#hash-nou";)
- input
- readystatechange
Evenimente ce nu isi gasesc o categorie
- invalid
- message
- open
Asteptarea unui eveniment
Functia addEventListener
Aceasta functie este folosita pentru a "astepta" daca un eveniment a fost declansat pe un obiect implicit al limbajului sau unul creat prin selectarea unei etichete HTML din pagina web.
Functia addEventListener va avea doi sau trei parametri, explicati in lista urmatoare.
- numeEveniment - obligatoriu - eveniment "asteptat" sa apara pe obiect
- functie - obligatoriu - functie ce va fi executata odata ce evenimentul asteptat s-a produs, aceasta functie poate fi o functie anonima, functie "sageata" sau o functie/metoda declarata in alta parte a codului iar in acest ultim caz trebuie specificat numele acesteia
- eveniment "capturat" - optional - valoare booleana (false este implicit) ce reprezinta daca evenimentul asteptat este declansat in faza de capturare/declansare (true) sau in faza de propagare (false) - evenimentele declansate intr-o eticheta HTML (devenit obiect prin selectarea acestuia din DOM) se pot "propaga" si la elementul "parinte", "bunic", "strabunic" s.a.m.d.
window.addEventListener("click", function(){ console.log("Eveniment declansat pe obiectul window."); // restul de cod });
Declansarea unui eveniment personalizat
Clasa Event
Aceasta clasa este folosita pentru a crea un eveniment personalizat. Clasa Event va fi instantiata intr-o variabila-obiect si va avea ca parametru numele in sir de caractere al evenimentului personalizat.
Functia (metoda) dispatchEvent
Aceasta functie reprezinta "declansatorul" evenimentului personalizat. Functia va avea ca parametru numele variabilei-obiect ce reprezinta instantierea clasei Event.
let numeEvenimentPersonalizat = "The Winter is Comming", varObjPersonalizat = new Event(numeEvenimentPersonalizat); window.addEventListener(numeEvenimentPersonalizat, function(){ console.log(`Evenimentul "${numeEvenimentPersonalizat}" a fost declansat.`); // restul de cod }); window.dispatchEvent(varObjPersonalizat);
Observatie: Functiile addEventListener si dispatchEvent sunt functii "atribuite" implicit de interpretorul de limbaj JavaScript in urmatoarele situatii:
-
obiectelor literare
- ex. window, document
-
obiectelor create dinamic in cod
- ex. let elm = document.createElement("numeEtichetaHTML");
-
prin selectarea unei etichete HTML din DOM
- ex. let elm = document.querySelector("numeEtichetaHTML sau selector CSS")
JavaScript ASINCRON
Concept general
Browserele "citesc" scenariile de cod de la stanga la dreapta si de sus in jos. In acest mod sunt si executate liniile de cod din scenariul / scenariile de cod ce pot fi intr-un fisier sau mai multe. Daca sunt adaugate mai multe fisiere in documentul HTML, acestea sunt "citite" si executate pe rand.
Programarea asincron presupune executarea unui scenariu de cod atunci cand "executantul" (browserul prin interpretorul de limbaj) nu este ocupat cu alt scenariu de cod ori atunci cand un eveniment asincron s-a produs. Practic, scenariul de cod "sta" si asteapta intr-o functie ca browserul sa termine ce are de facut sau evenimentul asincron sa fie declansat. Restul liniilor de cod ce fac parte din restul scenariul de cod putand fi executat / rulat.
Functiile setTimeout, clearTimeout, setInterval si clearInterval
Functiile setTimeout si setInterval vor putea executa asincron cate o functie o singura data sau de mai multe ori. Executia acestor functii se va face dupa un anumit timp sau la un anumit interval de timp. Acest timp va fi in milisecunde si reprezentat de o valoare numerica naturala.
Observatie: O secunda are 1000 de milisecunde.
Rolul functiilor clearTimeout si clearInterval este de a opri din executie functiile setTimeout si setInterval.
Functia setTimeout
Aceasta functie va avea doi parametrii. Primul este reprezentat (cel mai adesea) de o functie anonima iar al doilea parametru reprezinta o valoare numerica ce va reprezenta timpul dupa care functia anonima va fi executata.
// Exemplu 1 setTimeout(()=>{ console.log(14000); }, 500); // Exemplu 2 console.log("a"); setTimeout(()=>{ console.log("b"); }, 0); console.log("c"); // a // c // b // 14000
Functia clearTimeout
Aceasta functie va opri din executie functia setTimeout. Functia clearTimeout va avea va parametru numele variabilei ce are ca valoare functia setTimeout.
let setT = setTimeout(()=>{ console.log("Text ce nu va fi afisat in consola."); }, 0); clearTimeout(setT);
Functia setInterval
Functia setInterval va executa o functie (cel mai adesea anonima) ce este prezenta ca prim parametru la un anumit interval de timp pe o perioada nelimitata sau pana ce va fi "oprita" de la executie cu functia clearInterval. Intervalul de timp este prezent in parametrul doi al functiei si reprezinta o valoare numerica naturala.
let i = 0; setInterval(()=>{ console.log(++i); }, 500);
Functia clearInterval
Aceasta functie va opri din executie functia setInterval. Functia clearInterval va avea ca parametru numele variabilei ce are ca valoare functia setInterval.
let i = 0, interval = setInterval(()=>{ if(i == 10){ clearInterval(interval); } console.log(++i); }, 500);
Observatii in folosirea functiilor setInterval si setTimeout
-
atentie la rata de reimprospatare a ecranului
- nu folositi intervale de timp pentru a executa o functie ce actualizeaza grafica ecranului dispozitivului mai mica decat rata de reimprospatare a ecranului - aceasta rata este data de performantele hardware ale dispozitivului si nu poate fi incalcata
- majoritatea dispozitivelor inteligente au rata minima de reimprospatare a ecranului dupa minim 4 milisecunde (o secunda are 1000 milisecunde) - recomandat este sa folositi un interval minim de 4 ori mai mare pentru a actualiza grafica ecranului
Functiile requestAnimationFrame si cancelAnimationFrame
Functia requestAnimationFrame
Functia requestAnimationFrame este folosita pentru a oferi o performanta sporita atunci cand o functie este apelata de multe ori intr-un interval scurt de timp. Avantajele folosiri acestei functii sunt urmatoarele:
- browserul poate optimiza apelul functiei iar daca este folosita pentru a actualiza ecranul dispozitivului, tranzitia / animatia va fi mai lina
- tranzitiile / animatiile din alte ferestre sau taburi vor fi oprite, acest lucru ducand la o performanta sporita prin optimizarea resurselor dispozitivului
- economiseste curent
Este recomandat ca aceasta functie sa fie folosita recursiv. Prima data in afara functiei apelate de functia requestAnimationFrame iar a doua oara in corpul functiei apelate. In acest mod vom lasa la latitudinea browserului cand sa apeleze functia primita ca parametru de catre functia requestAnimationFrame.
function numeFunctie(){ // bloc de cod requestAnimationFrame(numeFunctie); } requestAnimationFrame(numeFunctie);
Functia requestAnimationFrame atribuie un parametru cu o valoare numerica functiei pe care o apeleaza iar acesta reprezinta momentul cand aceasta functie a fost executata.
let numeVariabila; function numeFunctie(timp){ // bloc de cod console.log(timp); numeVariabila = requestAnimationFrame(numeFunctie); } requestAnimationFrame(numeFunctie);
Observatie: Functia requestAnimationFrame returneaza o valoare numerica diferita de zero.
Functia cancelAnimationFrame
Functia cancelAnimationFrame este folosita pentru a opri din executie functia requestAnimationFrame. Acest proces se poate realiza prin declararea functiei requestAnimationFrame ca valoare pentru o variabila iar mai apoi aceasta variabila este atribuita ca parametru pentru functia cancelAnimationFrame.
let numeVariabila; function numeFunctie(timp){ // bloc de cod console.log(timp); numeVariabila = requestAnimationFrame(numeFunctie); } requestAnimationFrame(numeFunctie); setTimeout(()=>{ cancelAnimationFrame(numeVariabila); }, 2000);
Functia Promise
Unul dintre cele mai puternice caracteristici ale limbajului JavaScript este reprezentat de modul foarte usor prin care poate executa scenariul de cod in mod asincron. Acest limbaj a fost initial creat pentru Web si s-a dorit inca de la inceputurile sale sa ruleze cod asincron bazat pe evenimentele declansate in browser de catre utilizator.
Platforma Node.js (JavaScript pe server) preia aceasta caracteristica a limbajului si o duce la un alt nivel in sensul ca faciliteaza o mai buna inlantuire a metodelor si prin asta se doreste o alternativa mai buna la programarea asincron bazata pe evenimente.
In ultimi ani tot mai multi programatori isi dezvolta programele cu scopul ca acestea sa ruleze asincron. Modalitatea asincron bazata pe evenimente devine mai putin populara pentru ca nu mai poate indeplini tot ceea ce programatorii doresc sa faca. Din acest motiv functia Promise reprezinta solutia la aceasta problema.
Promise reprezinta modul prin care un bloc de cod dintr-o metoda inlantuita poate fi executat mai tarziu iar daca aceasta returneaza o eroare, o alta metoda inlantuita sau metoda urmatoare poate "prinde" aceasta eroare si poate sa faca ceva cu ea. In caz contrar, adica daca totul a mers bine, un alt bloc de cod dintr-o alta metoda inlantuita poate fi executat.
Ciclul de viata pentru Promise
Fiecare executie a functiei Promise presupune un ciclu de viata care incepe cu statusul de pending. Aceasta inseamna ca executarea codului in mod asincron nu a fost incheiata si nu avem o solutie ori un raspuns de la functia Promise. Imediat ce functia Promise termina de executat codul din interior, statusul se schimba in fulfilled sau rejected ceea ce inseamna ca in cazul primului status executia intregului cod a fost dusa la bun sfarsit iar in cazul al doilea operatia de executie a codului nu a putut fi dusa la bun sfarsit din cauza unei erori sau din alta cauza.
Proprietatea interna [[PromiseState]] este cea care poate sa aiba cele trei stari descrise anterior. Aceasta nu este accesibila in obiectul Promise insa ne putem folosi de metoda then() pentru a executa un cod in functie de ce valoare are proprietatea interna [[PromiseState]].
Metoda then() este prezenta in toate obiectele Promise si poate avea doua argumente. Primul argument este o functie anonima ce va contine codul de executat atunci cand statusul este fulfilled iar al doilea este o alta functie anonima ce va executa codul din interior atunci cand statusul este rejected.
let promise = new Promise(function(callbackSucces, callbackError){ callbackSucces(1); }); promise.then( function(value){ console.log(value); // 1 throw new Error('Eroare'); }, function(value){ console.log(value); // Error: Eroare } );
Observati ca atunci cand folosim functia Promise, aceasta trebuie instantiata. Functia va avea un singur parametru reprezentat de o functia anonima ce la randul ei va avea cele doua functi anonime ce vor fi executate in functie de valoarea statusului proprietatii [[PromiseState]].
In codul de mai sus prima functie anonima isi termina de executat codul si la sfarsit intentionat returneaza o eroare. Prin aceasta eroare si cea de a doua functie anonima este executata pentru ca o eroare a fost intampinata in executarea codului.
Oricare dintre aceste functii anonime sunt optionale. Daca vrem sa avem doar functia anonima pentru statusul de "succes", putem omite scrierea celei de a doua functii anonime insa eroarea tot este declansata insa nu mai este executata nici-o functie in acest sens pentru ca nu am declarat-o. Pentru asta va trebui sa inlantuim de metoda then() metoda catch(). Aceasta va avea ca parametru tot o functie anonima ce va avea un parametru obiect care va contine proprietatea message cu mesajul de la metoda de "succes" care ne declanseaza respectiva eroare.
let promise = new Promise(function(callbackSucces, callbackError){ callbackSucces(1); }); promise.then( function(value){ console.log(value); // 1 throw new Error('Eroare'); } ).catch(function(err){ console.log(err.message); // Eroare });
Interesant la metoda then() este ca putem avea oricate ne dorim. Fiecare poate returna celeilante o valoare sau o eroare de orice fel si in functie de asta scenariul de cod se desfasoara dupa cum ne dorim.
Fiecare apelare a metodei then() ne returneaza un alt Promise, fie ca este cel prezent ori altul declarat intr-o alta variabila.
let promise = new Promise(function(callbackSucces, callbackError){ callbackSucces(1); }); promise.then(function(value){ console.log(value); // 1 return ++value; }).then(function(value){ console.log(value); // 2 return ++value; }).then(function(value){ console.log(value); // 3 throw new Error('Eroare'); }).catch(function(err){ console.log(err.message); // Eroare });
Sistemul de programare asincron folosind obiectul Promise este mai eficient decat cel bazat pe evenimente pentru ca evenimentele tind sa nu returneze vreo eroare atunci cand acestea se intampla. Chiar daca returneaza o eroare de cele mai multe ori trebuie sa verifici unde este problema in blocul de cod si apoi sa o rezolvi. In cazul erorilor ce au loc folosind obiectul Promise, erorile pot fi interceptate usor si in mod silentios si de aceea este recomandat sa avem macar o metoda sau o functie anonima care sa "prinda" eroarea atunci cand aceasta este declansata.
In versiunile recente de ECMAScript s-a introdus si functia finally() ce la fel ca then si catch() va avea o functie anonima. Aceasta este utila atunci cand vrem sa facem "curat" dupa noi in cod.
let p = new Promise(function(resolve, reject){ resolve(1); }); p.then(function(n){ console.log(n); throw new Error('Eroare'); }).catch(function(err){ console.log(err.message); }).finally(function(){ console.log('Terminat'); }); // 1 // Eroare // Terminat
Metodele resolve() si reject()
Aceste metode reprezinta metode ce fac parte din obiectul Promise si sunt identice cu cele pe care le-am denumit generic in codurile anteriore ca fiind callbackSucces si callbackError.
Folosind aceste metode vom putea scrie mai putin cod daca scenariul de cod nu este deloc complex si avem nevoie doar sa facem un lucru simplu.
let promise1 = Promise.resolve(1); promise1.then(function(value){ console.log(value); // 1 }); let promise2 = Promise.reject('Eroare'); promise2.then(null, function(value){ console.log(value); // Eroare });
Extinderea prin mostenire
Vreau sa va arat un exemplu prin care putem extinde functionalitatea obiectului Promise printr-o clasa declarata de catre noi. Este un bun exercitiu dar si o modalitate buna de a extinde capabilitatile obiectului Promise. Nu spun mai multe pentru ca vreau sa va las sa intelegeti singuri codul.
class X extends Promise{ success(resolve, reject){ return this.then(resolve, reject); } fail(reject){ return this.catch(reject); } } let promise = new X(function(resolve, reject){ resolve(1); }); promise.success(function(value){ console.log(value); throw new Error('Eroare'); }).fail(function(err){ console.log(err.message); }); // 1 // Eroare
Evenimentele unhandledrejection si rejectionhandled
Aceste evenimente pot fi declansate pe platforma Node.js dar si in browser. In Node.js sunt declansate de obiectul process iar in browser sunt declansate de obiectul window. Totusi la data la care am testat al doilea eveniment in browser, acesta nu putea fi declansat.
Aceste evenimente sunt declansate in aceste situatii:
- unhandledrejection - declansat atunci cand este apelata metoda reject() din obiectul Promise declansand o eroare iar in cod nu avem functia anonima care sa se ocupe de aceasta eroare
- rejectionhandled - declansat atunci cand obiectul Promise intampina o eroare si trebuie apelata metoda reject() (sau cu numele generic callbackError) iar aceasta este declarata in cod
let promise = Promise.reject('Eroare'); promise.then(function(){}); window.addEventListener('unhandledrejection', function(){ console.log('Nu exista functie care sa se ocupe de eroare'); // Nu exista functie care sa se ocupe de eroare });
Metodele Promise.all() si Promise.race()
Rolul acestor metode este de a simplifica si urmari mai atent "promisiunile" ce sunt executate.
Metoda Promise.all()
Aceasta metoda accepta un singur argument iar acesta trebuie sa fie iterabil. Asadar vom declara o matrice ce va contine mai multe instantieri ale metodei Promise() sau cu alte cuvinte, mai multe "promisiuni".
let promise1 = new Promise(function(resolve, reject){ resolve(1); }), promise2 = new Promise(function(resolve, reject){ resolve(2); }), promise3 = new Promise(function(resolve, reject){ resolve(3); }); let promise4 = Promise.all([promise1, promise2, promise3]); promise4.then(function(value){ if(Array.isArray(value)){ // true console.log(value[0]); // 1 console.log(value[1]); // 2 console.log(value[2]); // 3 } });
In cazul in care una dintre "promisiuni" returneaza o eroare, acea eroare este afisata imediat iar restul de cod din "promisiuni" sunt respinse.
let promise1 = new Promise(function(resolve, reject){ reject(1); // 1 }), promise2 = new Promise(function(resolve, reject){ resolve(2); }), promise3 = new Promise(function(resolve, reject){ resolve(3); }); let promise4 = Promise.all([promise1, promise2, promise3]); promise4.then(function(value){ if(Array.isArray(value)){ // false console.log(value[0]); console.log(value[1]); console.log(value[2]); } });
Metoda Promise.race()
Metoda Promise.race() este foarte asemanatoare cu metoda Promise.all() insa spre deosebire de aceasta, metoda Promise.race() va returna o valoare pe care oricare dintre "promisiuni" o va face prima.
let promise1 = Promise.resolve(1), promise2 = new Promise(function(resolve, reject){ resolve(2); }), promise3 = new Promise(function(resolve, reject){ resolve(3); }); let promise4 = Promise.race([promise1, promise2, promise3]); promise4.then(function(value){ console.log(value); // 1 });
AJAX - Asynchronous JavaScript and XML
AJAX reprezinta o caracteristica foarte importanta in limbajul JavaScript. Cu aceasta putem efectua o cerere HTTP catre server pentru a actualiza o pagina web la diferite evenimente fara a reincarca pagina.
Functia XMLHttpRequest
Functia XMLHttpRequest reprezinta "nucleul" AJAX. Modalitatea prin care se efectueaza o cerere HTTP se poate face in felul urmator:
- se instantiaza functia XMLHttpRequest intr-o variabila obiect
- pe variabila obiect se asteapta declansarea evenimentului readystatechange, (eveniment declansat la schimbarea valori proprietatii readyState din variabila obiect) si odata ce acest eveniment a fost declansat, este executata o functie
- in cadrul functiei executate, se verifica (folosind o structura conditionara) daca valorile proprietatilor readyState si status au valorile 4 si respectiv 200 si in caz de adevarat pe ambele, vom stii ca raspunsul serverului este primit iar acesta se afla intr-o alta proprietate din variabila obiect iar aceasta este responseText
- in corpul structurii conditionare vom adauga raspunsul serverului in locul din pagina unde dorim sa afisam acest raspuns si pentru asta vom folosi proprietatea innerHTML de pe obiectul selectat din pagina
- in afara acestei functii vom apela metoda open, metoda ce va avea urmatorii parametri
- un sir de caractere reprezentand metoda prin care datele sunt trimise catre server (GET, POST, HEAD, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH)
- calea relativa sub forma unui sir de caractere catre fisierul de pe server unde datele sunt receptionate si se asteapta raspuns
- optional - valoare booleana daca datele sa fie trimise sincron sau asincron, implicit este valoarea false pentru modul sincron
- dupa apelarea metodei open se apeleaza metoda send (din variabila obiect) fara parametri
let xhr = new XMLHttpRequest; xhr.onreadystatechange = function(){ if(this.readyState == 4 && this.status == 200){ let htmlObjSelected = document.querySelector(selectorCSS); htmlObjSelected.innerHTML = this.responseText; } } xhr.open("POST", "calea relativa catre fisierul de pe server", true); xhr.send();
Membrii ai obiectului (metode si / sau proprietatii).
Functia EventSource
Functia EventSource reprezinta posibilitatea de a actualiza continutul unei pagini, fara a o reincarca, cu continut de pe server la un anumit interval de timp (implicit este de minim 3 secunde). Pentru a realiza acest lucru, functia EventSource foloseste o conexiune HTTP cu flux de evenimente (text/event-stream) continua cu fisierul de pe server. Calea catre acest fisier este prezenta sub forma de parametru atunci cand functia EventSource este instantiata.
Datele prin aceasta conexiune sunt doar dinspre server. Cand sunt trimise date dinspre server, in browser este declansat evenimentul message. Cand acest eveniment este declansat, vom executa o functie anonima. Aceasta functie anonima va avea un argument obiect cu proprietatea data. Aceasta proprietate va contine informatia de pe server.
Atunci cand conexiunea cu fisierul de pe server este creata, este declansat evenimentul open pe variabila obiect ce instantiaza functia EventSource. Daca dorim sa inchidem aceasta conexiune va trebui sa apelam metoda close de pe variabila obiect ce instantiaza functia EventSource.
// Server - Continut fisier, cod PHP <?php header('Content-Type: text/event-stream'); $time = date('r'); echo "data: {$time}"; ?> // Browser - Continut fisier, cod HTML si JavaScript <div class="container"></div> <script> let evs = new EventSource("calea-relativa-catre-fisierul-de-pe-server.php"), container = document.querySelector(".container"); evs.onmessage = (e)=>{ let div = document.createElement("div"); div.innerHTML = e.data; container.appendChild(div); } </script>
Membrii ai obiectului (metode si / sau proprietatii).
Functia fetch
Functia fetch reprezinta o alta modalitate de a trimite o cerere catre server pentru a actualiza o parte din pagina. Avantajul folosirii functiei fetch este ca aceasta returneaza un obiect Promise si putem astfel sa "legam" de functia fetch un "lant" de functii then dar si o functie catch (la sfarsitul acestui "lant") pentru situatia unei erori. Functia fetch va avea obligatoriu un parametru si optional doi parametrii.
Parametrii functiei fetch
Primul parametru poate fi:
- un sir de caractere ce reprezinta calea catre fisierul de pe server de unde vom astepta un raspuns sau incarcam o resursa
- un obiect cerere ce reprezinta instantierea functiei Request - aceasta functie poate avea aceeasi parametri ca functia fetch
Al doilea parametru - optional
- un obiect JSON cu una sau mai multe chei din urmatoarea lista:
- method - GET (implicit), POST, PUT, DELETE, etc. - IMPORTANT: Daca sunt folosite metodele GET sau HEAD, in cadrul obiectului JSON nu putem avea cheia body
- mode - cors (implicit), no-cors, same-origin
- cache - default (implicit), no-cache, reload, force-cache, only-if-cached
- credentials - same-origin (implicit), include, omit
-
headers - un JSON cu una din valorile urmatoare sau o instanta a functiei Headers
- 'Content-Type': 'application/json'
- 'Content-Type': 'application/x-www-form-urlencoded'
- redirect - follow (implicit), manual, error
- referrer - 'about:client' (implicit), un sir de caractere gol
- referrerPolicy - no-referrer-when-downgrade (implicit), no-referrer, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
- body - un obiect reprezentand instantierea uneia dintre urmatoarele functii: Blob, BufferSource, FormData, URLSearchParams, USVString, ReadableStream
- integrity
- keepalive
- signal
Functia fetch returneaza un obiect Promise ce la randul lui returneaza un alt obiect
Pentru a vedea acest obiect returnat, in functia anonima din primul then (functia anonima va primii sub forma de argument acest obiect) vom afisa in consola browserului acest obiect.
// Server - Continut fisier, cod PHP <?php $time = date('r'); echo "data: {$time}"; ?> // Browser - Continut fisier, cod HTML si JavaScript <button>Clic pentru mai multe detalii</button> <div id="container"></div> <script> let btn = document.querySelector("button"), container = document.querySelector("div"); btn.addEventListener("click", function(){ fetch("main.php") .then(function(response){ // vezi obiectul response // in consola browserului console.log(response); return response.text(); }) .then(function(value){ let div = document.createElement("div"); div.innerHTML = value; container.appendChild(div); }) .catch(function(error){ console.log(error); }); }); </script>
Membrii obiectului (metode si / sau proprietatii) Request.
Membrii obiectului (metode si / sau proprietatii) Response.
Programarea asincrona cu async si await
Cuvintele rezervate async si await sunt introduse in limbaj odata cu versiunea 8 a limbajului. Cuvintul rezervat async returneaza un Promise.
Sintaxa
Cuvantul rezervat async este folosit atunci cand o functie, de orice tip, este definita. Acest cuvant va fi adaugat inaintea cuvantului function (functie cu nume sau functie anonima), inaintea numelui functiei (metoda clasei) sau inaintea unei functii sageata.
Cuvantul rezervat await va putea fi folosit in cadrul functiilor de orice tip daca au specificat explicit cuvantul rezervat async. Rolul acestui cuvant este de a pune "pauza" executiei unui bloc de cod pana ce Promise-ul returnat de aceasta functie sau alta functie ce returneaza tot un Promise returneaza fulfilled.
Situatii in care putem folosi cuvantul rezervat await
- inaintea apelarii unei alte functii
- in cadrul unei structuri repetitive for... of
Exemple cu async si await
Ordinea afisarii rezultatelor este diferita daca sunt executate toate exemplele in acelasi timp.
// Exemplul 1 async function calc(a, b){ return a + b; } calc(14000, 41).then(valoare => console.log(valoare)); // 14041 // Exemplul 2 async function sumaValoriMatrice(matrice){ let n = 0; for await(let i of matrice){ n += i; } return n; } sumaValoriMatrice([1,2,3,4]).then(valoare =>console.log(valoare)); // 10 // Exemplul 3 console.log(99); async function afiseazaValori(){ let x = [1,2,3]; for await(let n of x){ console.log(n); } } afiseazaValori(); console.log(100); // 99 100 1 2 3 // Exemplul 4 class Angajat{ constructor(numeComplet, varsta, functie){ this.numeComplet = numeComplet; this.varsta = varsta; this.functie = functie; } async afiseazaInfo(){ return { numeComplet: this.numeComplet, varsta: this.varsta, functie: this.functie } } } let angajat = new Angajat("vali valentin", 14000, "Arhitect"); // metoda console.log va fi apelata pentru a se "ocupa" de ceea ce returneaza metoda obiectului angajat.afiseazaInfo().then(console.log); // {numeComplet: "vali valentin", varsta: 14000, functie: "Arhitect"} // Promise {<fulfilled>: undefined}
DOM - DOCUMENT OBJECT MODEL
Introducere in DOM
DOM este un standard W3C si reprezinta o interfata pentru diferite limbaje de programare (JavaScript, Python) pentru a accesa structura arborescenta a fisierelor ce contin etichete HTML si/sau XML. Altfel spus, DOM reprezinta intreaga structura a etichetelor din fisier.
In momentul cand fisierul ce contine aceste etichete, este incarcat complet in browser, acesta din urma creeaza obiectul DOM. In JavaScript, DOM-ul se "regaseste" in obiectul document.
Obiectul document
Obiectul document apartine obiectului window si putem sa il scriem cu sau fara obiectul window.
// varianta 1 console.log(window.document); // varianta 2 - preferabil console.log(document);
Obiectul document contine diferite metode pentru a accesa, crea, adauga sau sterge diferite etichete din DOM. Toate aceste metode returneaza intotdeauna un obiect.
Metode pentru a accesa etichete din DOM
Urmatoarele metode returneaza un obiect sau o colectie de obiecte foarte asemanatoare cu o matrice fara a fi insa o matrice. In cazul in care metoda folosita nu poate "gasi" eticheta/etichetele tinta, aceasta metoda returneaza valoarea null.
- document.getElementById() - selecteaza o eticheta ce are atributul id si o valoare anume, aceasta valoare este data metodei sub forma de sir de caractere
- document.getElementsByName() - selecteaza toate etichetele ce contin atributul name si o valoare, valoarea este data metodei sub forma de sir de caractere
- document.getElementsByTagName() - selecteaza toate etichetele dupa un anumit nume
- document.getElementsByClassName() - selecteaza toate etichetele dupa atributul class, atribut cu o anumita valoare ce este dat ca parametru acestei metode
- document.querySelector() - selecteaza o eticheta dupa un selector CSS, acest selector CSS este transmis metodei sub forma de sir de caractere
- document.querySelectorAll() - selecteaza toate etichetele dupa un selector CSS, acest selector CSS este transmis metodei sub forma de sir de caractere
Important atunci cand folosesti metoda getElementById()
Pentru fiecare eticheta HTML ce contine atributul id si o valoare pentru acesta, JavaScript creaza automat o variabila cu numele / sirul de caractere adaugat ca valoare pentru atributul id. Aceasta variabila va avea ca valoare insusi eticheta HTML ce contine acest atribut id. Este ca si cum ai fi selectat explicit respectiva eticheta dupa valoarea atributului id al acesteia folosind metoda getElementById().
Exemplu
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title></title> </head> <body> <div id="test"></div> <script> console.log(test); // <div id="test"></div> </script> </body> </html>
Metoda pentru a crea o eticheta
- document.createElement() - aceasta metoda va avea ca parametru un sir de caractere reprezentand numele etichetei - metoda returneaza un obiect
let p = document.createElement('p'); console.log(p); // <p></p> console.log(typeof p); // object
Metode / proprietati pentru a adauga sau edita etichete in DOM
- appendChild() – adauga un obiect creat cu metoda document.createElement() dupa etichetele-copil din obiectul tinta
- textContent – adauga un sir de caractere obiectului creat cu metoda document.createElement()
- innerHTML – seteaza sau preia continutul unei etichete selectate
- insertBefore() – adauga un obiect "frate" inaintea celui selectat in DOM
- insertAfter() - adauga un obiect "frate" dupa obiectul selectat in DOM
- replaceChild() – aceasta metoda va putea fi folosita pe un obiect selectat din DOM si va avea doi parametri obiect, primul va fi "noul obiect" iar al doilea va fi "vechiul obiect sau obiectul ce va fi inlocuit" - aceasta metoda returneaza obiectul inlocuit
- cloneNode() – cloneaza un obiect cu toti descendentii
Metoda pentru a sterge un obiect din DOM
- removeChild() - aceasta metoda este folosita de un obiect-parinte selectat din DOM pentru a sterge un obiect-copil al acestuia primit ca parametru
Proprietati pentru a selecta obiecte din DOM
Aceste proprietati sunt folosite (cel mai adesea) pe obiectele din DOM selectate cu metodele specifice de selectare. Numele acestor proprietati vorbeste de la sine.
- parentNode
- firstElementChild
- firstChild
- lastElementChild
- lastChild
- childNodes
- children
- nextElementSibling
- previousElementSibling
Metode pentru lucrul cu atribute pe obiectele (etichetele) din DOM
- setAttribute()
- getAttribute()
- removeAttribute()
- hasAttribute()
Proprietati pentru stilizarea obiectelor (etichetele) din DOM
- style - proprietate-obiect a obiectului selectat ce seteaza sau preia valoarea unei proprietatii CSS
- className - proprietate a obiectului selectat ce seteaza sau preia o lista cu "clase" CSS
-
classList - proprietate-obiect a obiectului selectat ce contine mai multi membri insa cei mai importanti sunt urmatorii (semantica este la ea acasa):
- add()
- contains()
- remove()
- toggle()
Proprietati pentru a afla date despre obiectul selectat
- offsetWidth
- offsetHeight
- clientWidth
- clientHeight
Proprietati importante din DOM
- document.location
- document.URL
- document.activeElement
- document.all
- document.body
- document.charset
- document.contentType
- document.defaultView
- document.doctype
- document.domain
- document.execCommand
- document.exitFullscreen
- document.fonts
- document.fullscreen
- document.fullscreenEnabled
- document.getSelection
- document.images
- document.lastModified
- document.links
- document.readyState
- document.scripts
- document.styleSheets
- document.title
Membrii obiectului (metode si / sau proprietatii) document.
EXPRESII REGULATE
Notiunii fundamentale in expresiile regulate
Ce sunt expresiile regulate ?
Sa incepem prin a raspunde la intrebarea, ce sunt expresiile regulate ? Numele este opac si nu ne spune prea multe. Expresiile regulate fac referire doar la text. Gandeste-te putin si uita-te in jurul tau pentru a vedea cat de mult text este in lumea noastra digitala moderna: e-mail, stiri, mesaje text, date de pe piata de valori, un cod de computer, contacte din agenda, oamenii etichetati in fotografii - toate aceste lucruri sunt text. Expresiile regulate sunt un instrument care ne permite sa lucram cu aceste texte prin a descrie modele de text.
Asadar, o expresie regulata este un set de simboluri care descrie un model de text. Acum este la singular. Atunci cand il vedem pluralizat vorbim despre limba oficiala a acestor simboluri, simboluri care trebuie sa fie interpretate de un procesor de expresie regulata. Vom vorbi un pic mai mult despre procesoare intr-un moment insa acum este bine sa stiti ca procesorul este cel care face toata munca si fara el o expresie regulata nu va potrivi un text sau o bucata de text cu scopul de a manipula acel text. Acum sa ne gandim un moment si sa vedem ce sunt expresiile regulate si ce nu sunt. Expresiile regulate nu sunt un limbaj de programare chiar daca fac parte dintr-un limbaj de programare. Expresiile regulate sunt un pseudo-limbaj.
Cele mai multe limbaje de programare folosesc expresiile regulate. In expresiile regulate nu exista variabile si din acest motiv nu putem calcula 1 + 1. Expresiile regulate sunt doar simboluri care descriu un model de text si atat. In mod frecvent, veti auzi de termenul RegEx si reprezinta prescurtarea de la REGular EXpression (expresii regulate).
In continuare sa vorbim despre modurile in care s-ar putea folosi expresiile regulate pentru a lucra cu text. S-ar putea sa le utilizati pentru a testa daca un numar de telefon are numarul corect de cifre sau in cazul in care o adresa de e-mail este intr-un format valid. Ai putea cauta intr-un document cu text un cuvant scris cu sau fara o anumita litera, caracter special sau dupa caz o cifra cu scopul de a face o modificare asupra acelui text. De asemenea putem cauta un text daca acesta are dupa sau inaintea lui un alt text.
Ai putea folosi expresiile regulate pentru a converti un fisier delimitat prin fila intr-un fisier delimitat prin virgula sau pentru a gasi cuvinte duplicate intr-un text. In fiecare dintre aceste cazuri, vom utiliza o expresie regulata pentru a scrie o descriere a ceea ce cautam folosind simboluri. In cazul unui numar de telefon, acel model ar putea fi de trei cifre urmate de o liniuta, urmat de alte trei cifre si o alta linie si apoi urmat de alte patru cifre. Odata ce ne-am definit modelul nostru atunci procesorul de expresii regulate (regex) va folosi descrierea noastra pentru a returna rezultatele potrivite sau in cazul testului sa intoarca adevarat sau fals daca a gasit sau nu o potrivire in textul tinta.
O expresie regulata se potriveste cu textul in cazul in care descrie in mod corect textul si pentru asta va trebui sa invatati mai tarziu simbolurile folosite in expresiile regulate insa pana atunci hai sa va spun cate ceva despre istoria expresiilor regulate.
Istoria expresiilor regulate
In aceasta sectiune vom arunca o privire asupra istoriei expresiilor regulate. Cred ca partea cea mai surprinzatoare a istoriei expresiilor regulate este ca au avut prima aparitie in domeniul neurologiei undeva prin anii 1940. In 1943, McCulloch si Pitts au dezvoltat modele care descriu modul in care functioneaza sistemul nervos uman sau modul in care o masina sau un calculator ar putea fi construit pentru a actiona mai mult ca un creier uman. In 1956, Stephen Kleene a descris aceste modele cu o algebra pe care el a numit-o seturi regulate si ulterior le-a numit expresii regulate.
Asadar numele de expresii regulate este dat de Stephen Kleene si este cel care le-a dat "viata" iar la momentul respectiv acestea nu faceau parte din era digitala. In anul 1968, Ken Thompson un pionier al calculatoarelor si unul dintre dezvoltatorii cheie ale sistemului de operare UNIX au pus in aplicare expresiile regulate in interiorul unui editor de text UNIX si pe care el l-a construit si i-a dat denumirea ED. Acesta este punctul in care expresiile regulate au intrat in lumea calculatoarelor si se intampla chiar acolo, la nasterea sistemului de operare UNIX. Asadar sistemul de operare UNIX si expresiile regulate au pornit in acelasi timp in era digitala.
Acum daca ai fi un utilizator al acestui editor de text (ED) si ai vrea sa cauti un text cu ajutorul unei expresii regulate, acest lucru s-ar face prin tastarea literelor →ag← si apoi →/←, iar apoi setul de simboluri care formeaza expresia regulata pentru ceea ce ai vrut sa cauti si la final din nou →/← si litera →p←. Literele →g← si →p← au fost si raman modificatori. Litera →g← reprezinta acronimul de la cuvantul global si reprezinta cautarea la nivel global in textul tinta iar →p← reprezinta acronimul de la print cu referire directa la a printa / afisa rezultatele pe ecran. Asadar inaintea acronimului →re← (regular expression) s-a adaugat litera →g← (global) si la sfarsitul acronimului s-a adaugat litera →p← (print) si asa a luat nastere un alt acronim grep si dupa cum va dati seama reprezinta Global Regular Expression Print.
In lumea UNIX se intelege rapid termenul de grep si inseamna ca doresti sa cauti cu ajutorul unei expresii regulate. Termenul de grep a devenit atat de popular incat a devenit de fapt un limbaj de sine statator, astfel incat ai putea cauta in sistemul de fisiere UNIX un fisier sau mai multe fisiere cu o anumita denumire si din acest motiv a devenit foarte util in alte programe UNIX.
Mai mult decat atat, sistemul UNIX si grep au devenit atat de populari incat au fost introdusi in universitati iar aceste universitati au predat apoi urmatoarea generatie de "stele pe calculator" folosind sistemul UNIX. Asadar expresiile regulate nu numai ca au ajutat la raspandirea sistemului UNIX dar sau ajutat si pe ele insele in acest fel. De-a lungul anilor 1970 sistemul UNIX devine atat de popular incat incepe sa evolueze si odata cu el si expresiile regulate iar din acest motiv apar primele probleme cu expresiile regulate. Acum exista o problema in evolutia limbajului ce defineste expresiile regulate si asta pentru ca avem un set de simboluri care definesc in mod clar ceva care se potriveste si ceva care nu se potriveste.
Ei bine, daca incepeti sa schimbati sintaxa acestor simboluri, atunci vom crea probleme de compatibilitate. Imaginati-va daca ati avut un caracter de baza, care nu a avut o semnificatie speciala intr-o singura versiune, dar apoi intr-o versiune viitoare dintr-o data ii dati o alta semnificatie. Toate acele expresii regulate nu ar mai functiona si nu s-ar mai potrivi cu motoarele vechi de interpretare si nu ar mai fi in masura sa proceseze noile expresii regulate. In plus, fata de grep, una dintre primele modificari este ca s-a introdus un nou program numit EGREP sau grep extins. Puteti obtine, de fapt, acelasi comportament de egrep in interiorul grep folosind optiunea E dupa grep.
Asa ca grepe este in esenta acelasi lucru ca si egrep. Pe masura ce se adauga noi functionalitati in limbaj si lucreaza tot mai multi programatori la acest limbaj, putem spune ca de aici apar primele incompatibilitati iar in 1986, toata lumea care era implicata in dezvoltarea limbajului, propune un standard, pe care il numesc POSIX si reprezinta acronimul de la Portable Operating System Interface Unix iar litera X este de la sistemul de operare UNIX.
Asa ca POSIX este un standard care este proiectat pentru a asigura compatibilitatea intre diferite sisteme de operare. Deci, primul lucru pe care il face este ca vor fi doua tipuri diferite de expresii regulate. Primul este reprezentat de BREs ce reprezinta acronimul de la Basic Regular Expressions (expresii regulate de baza) si in esenta este GREP si ERES ce reprezinta acronimul de la Extended Regular Expressions (expresii regulate extinse).
Acum, nu este de asteptat ca BREs si ERES sa fie interschimbabile, dar cel putin avem doua cai clare inainte. Termenul de BREs mai poarta denumirea prescurtata si de BRE (Basic Regular Expression) si este foarte folosit pentru a mentine compatibilitatea cu instrumentele vechi iar ERES este ceea ce instrumentele moderne vor folosi. Asa ca tot acest efort de a standardiza intr-un fel toate ideile implementate in expresiile regulate ne-au adus mai aproape de conceptul din prezent ce face referire la modul cum expresiile regulate ar trebui sa functioneze. In acelasi timp Henry Spencer scrie o librarie RegEx in limbajul de programare C.
Ce e minunat este faptul ca este o librarie si ca poate fi incorporata in alte programe si de aceea ofera consistenta pentru ca atunci toti cei care utilizeaza libraria, expresiile lor regulate vor functiona in acelasi mod. Asa ca lucrurile la acest moment au devenit mai consistente si modificarile expresiilor regulate s-au stabilizat. In anul 1987, Larry Wall creaza limbajul de programare Perl. Acesta foloseste libraria RegEx Spencer si de-a lungul timpului i se adauga multe caracteristici mai puternice iar adevarata misiune Perl a fost sa incerce si sa fie un limbaj de programare care a fost proiectat pentru a fi cu adevarat util.
Din cauza acestor caracteristici puternice, devine intr-adevar standardul de aur al modului in care oamenii doresc ca librariile lor RegEx sa lucreze. Multa lume scrie in acest moment alte limbaje de programare iar majoritatea doresc ca libraria standardizata sa functioneze asa cum Perl facut-o. Deci, toate aceste caracteristici incep sa se strecoare si avem limbaje si programe Perl compatibile. Asa ca C, C++, limbajele .NET, Java, JavaScript, MySQL, PHP, Python si Ruby se straduiesc sa fie compatibile cu limbajul de programare Perl. Exista o librarie numita librarie PCRE care este compatibila cu Perl si libraria Regular Expression.
Motoare sau interpretori de expresii regulate
Anterior am vazut cum expresiile regulate sau raspandit la un numar diferit de limbaje si cum aceste limbaje sunt diferite intre ele, pentru ca ruleaza in diferite medii, tot asa si expresiile regulate devin usor diferite chiar daca se incearca sa se pastreze standardele BRE (basic regular expression) si ERE (Extended Regular Expression) si in acest fel sa se pastreze ceea ce limbajul PERL a standardizat la expresiile regulate. Interpretoarele de expresii regulate nu au fost intotdeauna scrise de aceleasi persoane si din acest motiv acestea sunt putin diferite si pot interpreta diferit un anumit set de simboluri.
Limbajele C, C++, Java, JavaScript si ActionScript fac parte din ECMAScript si reprezinta acronimul de la →European Computer Manufacturers Association Script← si daca imi amintesc bine ECMAScript sustine standardul BRE. Limbajele .NET, Perl si PHP se bazeaza pe libraria PCRE →Perl Compatibil Regular Expression←. Exista o serie de programe diferite pe UNIX, unele care utilizeaza POSIX BRE si altele care utilizeaza POSIX ERE.
Dupa cum am spus BRE reprezinta expresii regulate de baza iar ERE reprezinta expresii regulate extinse. Prima versiune de Apache a folosit POSIX ERE iar versiunea Apache 2 foloseste libraria PCRE la fel ca si PHP, MySQL si ulterior POSIX ERE.
Fiecare interpretor de limbaj are incorporat in interior un motor sau interpretor de expresii regulate ce se doreste sa fie cat mai compatibil cu BRE sau ERE si odata cu trecerea timpului limbajele de programare sufera diferite modificari si aceste modificari pot influenta si modul in care expresiile regulate vor functiona.
Interpretor de expresii regulate pentru JavaScript
In prezent toate limbajele de programare au un interpretor care executa fiecare linie de cod intr-un anumit mediu. Acest interpretor are cel mai adesea incorporat si un motor cu ajutorul caruia interpreteaza simbolurile pentru expresii regulate scrise de noi in acel limbaj.
JavaScript ruleaza in prezent in medii diferite iar primul mediu in care a rulat a fost browserul. Browserele executa foarte rapid simbolurile din expresiile regulate si returneaza un rezultat cu care vom face ceva anume.
Exista un site web foarte bine facut cu scopul de a testa diferite expresii regulate in JavaScript iar acesta este www.regexpal.com. Va invit sa il vizitati si sa testati expresiile voastre regulate in limbajul JavaScript pe masura ce va voi prezenta diferite simboluri cu un scop precis sau daca stiti deja sa lucrati cu expresiile regulate, atunci il puteti folosi din acest moment.
Sintaxa
Sintaxa unei expresii regulate reprezinta modul in care acestea sunt declarate sau scrise si dupa cum stiti expresiile regulate contin doar simboluri din diferite caractere speciale, litere si / sau cifre. Acestea vor fi asezate cel mai adesea in interiorul a doua caractere slash.
Moduri de lucru
In expresiile regulate exista mai multe moduri de lucru si fiecare dintre ele este reprezentat de catre o litera anume adaugata dupa cel de al doilea slash.
Modul global
Modul global reprezinta cautarea la nivel global, adica in intreg textul tinta, a textului care se potriveste cu simbolurile din expresia regulata. Vom face asta prin adaugarea literei →g← dupa cel de al doilea slash.
Modul multiline
Modul multiline reprezinta cautarea pe mai multe randuri de text din textul tinta a textului care se potriveste cu expresia regulata si la fel ca la modul anterior vom adauga o litera dupa cel de-al doilea slash. Modul multiline este reprezentat de litera →m←.
Modul case sensitive
Modul case sensitive reprezinta modul de cautare in textul tinta dupa un anumit text care se potriveste cu expresia regulata dar este scris sau are incorporat una sau mai multe litere scrise cu litera mare si la fel ca si la celelalte moduri de lucru vom adauga o litera la sfarsitul ultimului slash. Litera reprezentand modul case sensitive este →i←.
Modul Unicode - Suport Unicode
Testarea pentru suportul Unicode
Acest lucru se poate realiza intr-o maniera foarte simplista si pentru asta ne vom folosi de structura try{} catch(){} si de instantierea functiei RegExp(), ambele in interiorul unei functii ce va returna o valoare booleana.
function hasRegExpU(){ try{ let pattern = new RegExp(".", "u"); return true; } catch(e){ return false; } }
Important la aceasta abordare este ca aceasta functie poate rula fara nici-o problema si in browserele mai vechi si care nu suporta modulul u.
Modul →u← in expresiile regulate
Executarea in modul →u← a unei expresii regulate permite testarea caracterelor Unicode, in special al acelora ce nu sunt in limba latina.
let text = 'ð ®·'; console.log(/^.$/.test(text)); // false console.log(/^.$/u.test(text)); // true
Functi noi de lucru pe siruri
Incepand cu standardul ECMAScript 6 avem la dispozitie cateva metode pe care le putem folosi dupa bunul plac. Desi aceste metode fac ceea ce numele lor ne spune, trebuie sa retinem ca la acest moment aceste metode nu suporta expresie regulata ca parametru iar pentru asta trebuie sa folosim un sir de caractere.
- include() - returneaza adevarat daca textul dat este gasit oriunde in sir. In caz contrar returneaza fals.
- beginWith() - returneaza adevarat daca textul dat se gaseste la inceputul sirului. In caz contrar returneaza fals.
- endWith() - returneaza adevarat daca textul dat se gaseste la sfarsitul sirului. In caz contrar returneaza fals.
Alte functii de lucru pe siruri:
- codePointAt()
- fromCodePoint() - primeste ca parametru o valoare numerica naturala si va returna caracterul corespondent acelei valori numerice
Proprietatile source si flags
Expresiile regulate de dupa ECMAScript 6 capata doua noi proprietati care nu fac altceva decat sa ne returneze ceea ce numele lor spun. Aceste proprietati sunt source si flags.
let reg = /^.*$/g; console.log(reg.source); // ^.*$ console.log(reg.flags); // g
Observatie: O expresie regulata poate lucra in acelasi timp cu toate aceste moduri si pentru asta puteti adauga toate literele reprezentand modul de lucru pentru fiecare in parte.
Simboluri
Caractere literare
Una dintre cele mai simple modalitati cu care putem sa potrivim o expresie regulata cu un anumit text este prin folosirea caracterelor literare. Aceste caractere literare nu sunt altceva decat diferite litere pe care le adaugam in expresia regulata pentru a potrivi un anumit set de litere din text si ca setare de baza acestea sunt case sensitive si este foarte important daca scriem aceste litere (simboluri) cu litera mare sau mica. In cazul in care vrem sa potrivim un text cu litera mare si mica atunci trebuie sa facem ca expresia regulata sa ruleze in modul case sensitive si vom face asta prin adaugarea literei →i← dupa cel de-al doilea slash.
In expresiile regulate spatiul dintre litere (simboluri) este foarte important si trebuie sa se tina cont de el daca vrem sa potrivim un cuvant ce (din greseala) contine un spatiu ce (nu) trebuie sa fie acolo.
Sa presupunem ca avem un text in care cuvantul Java se gaseste sub acesta forma sau sub forma JavaScript si despre care stim ca sunt doua limbaje total diferite si care au in comun doar acest cuvant Java.
In expresia regulata vrem sa potrivim cuvantul Java cu scopul de a-l sterge, pune sub o eticheta HTML sau orice altceva ne dorim. Pentru ca expresia sa potriveasca doar cuvantul Java fara Java din JavaScript va trebui sa adaugam un spatiu liber sau ce anume se afla dupa acest cuvant in text (un caracter special sau o cifra) dupa aceste simboluri literare ce reprezinta cuvantul Java.
/Java /i
In acest moment expresia regulata va potrivi primul text ce se potriveste cu simbolurile noastre din expresia regulata si daca mai avem in acelasi text cuvantul Java atunci pe acesta nu il va putea potrivi pentru ca expresiile regulate au ca setare de baza sa nu ruleze in modul global. Potrivirea pentru cazul in care sunt mai multe cuvinte sau text care se potrivesc cu expresia regulata se face intrand in modul global si pentru asta vom adauga litera →g← dupa cel de-al doilea slash.
Expresiile regulate sunt nerabdatoare sa returneze un rezultat dar in acelasi timp sunt si lacome pentru ca vor sa potriveasca cat mai mult si sa returneze ce a potrivit.
Metacaractere
Pana acum am vazut cum putem sa potrivim literar un caracter sau mai multe (un simbol sau mai multe) din expresia regulata cu ceea ce vrem sa potrivim in textul nostru tinta. Acum este timpul sa vorbim putin despre metacaractere.
Metacaracterele sunt diferite litere si caractere speciale care au un rol special in expresiile regulate. Acestea nu sunt multe ca numar iar unele au inaintea lor caracterul backslash →\←. Din moment ce numarul metacaracterelor nu este mare, ba mai mult, numarul lor este chiar mic, anumite metacaractere au mai multe roluri in functie de locul unde acestea se afla in expresia regulata.
Metacaracterul punct
Metacaracterul punct mai poarta denumirea si de metacaracter joker (wildcard). Acest metacaracter este unul dintre cele mai importante in expresia regulata. Pana acum am putut potrivi literar caracter cu caracter, spatiu cu spatiu, cuvinte scrise cu litera mare sau mica, cuvinte ce contin una sau mai multe litere scrise cu litera mica sau mare si care pot avea diferite caractere speciale incluse dar fara insemnatate in expresiile regulate.
Metacaracterul punct →.← poate potrivi orice caracter special care nu are o semnificarie speciala in expresiile regulate (si daca are atunci trebuie adaugat caracterul backslash inainte), litera scrisa cu majuscule ori minuscule, un spatiu sau o cifra.
Metacaracterul punct nu poate potrivi un caracter invizibil dar care este acolo si este reprezentat de rand nou sau linie noua (new line) →\n←. In cazul in care nu stiati acest caracter este adaugat de editoarele de text in momentul cand apasati tasta ENTER. Motivul pentru care acest metacaracter nu poate potrivi si →\n← este pentru ca la inceput expresiile regulate in sistemul UNIX erau facute sa ruleze doar pe o singura linie.
Este foarte important sa retineti ca metacaracterul punct va potrivi doar un singur caracter si daca vreti sa potriviti mai multe va trebui sa adaugati mai multe metacaractere punct insa nu va recomand asta pentru ca exista o varianta mai buna pentru a realiza acest lucru, insa despre asta va voi spune un pic mai tarziu.
Metacaracterul backslash
Metacaracterul backslash mai poarta denumirea si de caracter ce scapa, altereaza un rol sau schimba un rol (escape). Rolul acestui metacaracter vi l-am prezentat un pic mai devreme insa acum va spun mai multe despre el.
Asa cum am spus mai sus in expresiile regulate avem diferite metacaractere ce au diferite roluri in expresiile regulate. In textele noastre tinta putem gasi aceste metacaractere ce au o insemnatate doar in textul tinta. Pentru a potrivi expresiile noastre regulate cu diferite texte ce pot contine aceste metacaractere atunci trebuie sa adaugam metacaracterul backslash →\← inaintea metacaracterului din expresia regulata si in acest fel ii alteram rolul acestuia facandu-l sa para pentru interpretorul de expresii regulate doar un simbol simplu.
Metacaracterul backslash trebuie folosit doar pe metacaracterele carora vrem sa le alteram rolul, sa le schimbam ceea ce reprezinta in expresiile regulate si doar in interiorul unei expresii regulate si niciodata pe alte caractere (simboluri) din expresia regulata ce sunt alcatuite din litere pentru ca acestea au o insemnatate diferita sau mai bine spus un rol diferit si contin deja acest metacaracter.
Alte metacaractere
In expresiile regulate sunt cateva metacaractere care sunt reprezentate de litere iar inaintea lor este adaugat metacaracterul backslash, metacaracter ce ii schimba rolul din simbol literar intr-un cu totul alt rol.
Pana acum stim ca daca vrem sa potrivim un spatiu din textul tinta in expresia regulata va trebui sa adaugam un spatiu in expresia regulata. Exista o modalitate mai buna pentru a potrivi un spatiu cu ajutorul expresiilor regulate si nu doar atat iar mai jos puteti vedea diferite metacaractere ce vor potrivi un spatiu, o litera, un caracter special sau o cifra.
- \s - va potrivi un singur spatiu
- \S - va potrivi un caracter care nu este spatiu (opusul)
- \n - va potrivi un caracter invizibil ce reprezinta o noua linie (new line)
- \t - va potrivi un caracter tab
- \w - va potrivi un caracter ce poate fi litera, cifra sau semnul sublinie (en. underscore)
- \W - va potrivi un caracter ce nu poate fi litera, cifra sau semnul sublinie (en. underscore) (opusul)
- \d - va potrivi un caracter digital (o cifra)
- \D - va potrivi un caracter ce nu este digital (opusul)
- \b - va potrivi un grup de caractere litere, cifre si/sau semnul sublinie (en. underscore) (limita de cuvant - word boundaries)
- \B - va potrivi un grup de caractere ce nu este limita de cuvant (ex. interiorul unui cuvant) si uneori diferite caractere speciale (opusul)
Observatie: Important la metacaracterul \w
Acest metacaracter va potrivi si cifre pentru ca acestea sunt de fapt cifre sub forma de text si daca vrem sa potrivim doar cifre din text va trebui sa folosim metacaracterul →\d←. Un alt aspect foarte important la metacaracterul →\w← este ca va potrivi si caracterul special underscode →_←, acesta fiind interpretat ca fiind text.
Rolul parantezelor patrate in expresiile regulate
Parantezele patrate au un rol bine definit in expresiile regulate si daca vrem sa potrivim cu ajutorul expresiei regulate diferite paranteze patrate ce se afla in interiorul textului tinta atunci acestea trebuie sa aiba inaintea lor metacaracterul backslash.
In expresiile regulate rolul parantezelor patrate este acela de a potrivi un text ce va contine unul dintre simbolurile ce sunt in interiorul parantezelor patrate iar acestea pot fi o litera, cifra sau orice caracter special ce nu are un rol in expresia regulata si daca are atunci va trebui precedat de metacaracterul backslash.
Un exemplu util poate fi acesta in care o expresie regulata va potrivi ambele cuvinte ce contin un simbol ( o litera ) din interiorul parantezelor patrate si este important sa retineti ca nu este importanta ordinea in care aceste simboluri sunt prezentate in interiorul parantezelor patrate.
/[ai]nna/ // va potrivi anna si inna
Metacaracterul minus
Acest metacaracter este foarte util daca vrem sa potrivim un interval de simboluri ce reprezinta litere sau cifre.
Sa presupunem ca vrem sa potrivim in interiorul parantezelor patrate un text ce ar putea contine literele cuprinse intre a si f inclusiv. Pentru a putea realiza acest lucru avem la dispozitie doua variante. Prima ar fi aceea in care am scrie toate literele alfabetului de la a la f si cea de a doua este reprezentata de folosirea metacaracterului minus intre literele a si f.
/[a-f]/
Acelasi procedeu il vom folosi si daca vrem sa potrivim cifre din text cu ajutorul expresiilor regulate.
/[0-5]/
Putem face mai mult de atat cu acest metacaracter si putem potrivi si alte intervale de litere sau cifre si asta pentru ca primul simbol fie ca este vorba de o litera sau o cifra va fi simbolul de inceput iar cel de-al doilea va fi simbolul de sfarsit.
Un aspect foarte important este acela cand vrem sa potrivim cifre cu doua numere. Sa presupunem ca vrem sa potrivim cifrele cuprinse intre 11 si 20. In acest caz nu putem scrie in expresia noastra /[11-20]/ pentru ca nu va functiona si asta pentru ca metacaracterul minus va opera doar in intervalul 0 si 9 iar acestea nu sunt cifre ci doar text. Amintiti-va ce va spuneam la inceput cand vorbeam despre expresiile regulate, expresiile regulate fac referire doar la text si nimic altceva.
Metacaracterul de negatie sau negativ
Metacaracterul de negatie este un caracter special ce probabil l-ati vazut foarte rar. Acesta se afla situat pe linia orizontala cu cifre de la tastatura la cifra 6.
Metacaracterul de negatie este unul dintre caracterele speciale ce detine mai multe roluri in expresiile regulate iar pozitia acestuia intr-o expresie regulata este foarte importanta pentru ca in functie de aceasta va avea un anumit rol.
Anterior v-am vorbit de rolul parantezelor drepte in expresiile regulate. Am vazut ca in interiorul acestora putem adauga cel mai adesea simboluri ce reprezinta litere cu scopul de a potrivi diferite cuvinte ce contin unul din simbolurile aflate in interiorul parantezelor drepte.
Metacaracterul de negatie se adauga in interiorul parantezelor drepte si intotdeauna va fi primul adaugat si plasat in partea stanga si este reprezentat in expresiile regulate de caracterul special →^←.
Prezenta acestuia in interiorul parantezelor drepte ii va spune interpretorului de expresii regulate sa potriveasca text ce nu contine literele (simbolurile) aflate in interiorul parantezelor drepte si la fel ca si la exemplul de mai sus va contine simbolurile ce urmeaza dupa parantezele drepte.
/[^ai]nna/ // nu va mai potrivi anna si inna ci knna, qnna, onna s.a.m.d.
Metacaractere in interiorul parantezelor drepte
Metacaracterele in interiorul parantezelor drepte sunt scapate
de rolul pe care acestea il au in interiorul expresiilor regulate. Aceasta este regula generala pe care vreau sa o retineti. Totusi exista cateva metacaractere care fac exceptie si asta pentru rolul pe care il au in interiorul acestor paranteze drepte. Este logic sa fie asa pentru ca altfel nu ar mai avea rolul lor bine definit si nu am mai face nimic cu ele.
Dupa cum v-ati dat seama aceste caractere sunt →^ - \ ]← si pentru a putea fi scapate
de rolul pe care il au in interiorul parantezelor drepte acestea trebuie sa fie precedate de metacaracterul backslash.
Repetarea simbolurilor
Repetarea metacaracterelor
Va spuneam mai sus despre anumite caractere speciale care au un rol diferit in functie de pozitia acestuia in expresia regulata. Acum este timpul sa va spun mai multe si despre acestea.
Aceste caractere speciale nu sunt precedate de metacaracterul backslash pentru a le oferi acest rol. Simpla lor prezenta defineste un rol in expresia regulata. Daca vrem sa anulam acest rol acestor caractere speciale va trebui sa adaugam metacaracterul backslash inaintea lor si in acest fel le dam o insemnatate literara. Acum este timpul sa facem cunostinta cu aceste caractere speciale.
Caracterele speciale →+ * ?← vor extinde insemnatatea metacaracterelor ce sunt formate din caracterul backslash si o litera (fie ca este scris cu majuscule ori minuscule) sau caracterul special punct. Aceste caractere speciale ne vor ajuta sa nu mai scriem atat de mult o expresie regulata cu scopul de a potrivi un caracter sau mai multe, daca un caracter este prezent de zero ori sau de mai multe ori sau daca un caracter este prezent de zero ori sau o singura data. Practic aceste caractere speciale au aceasta insemnatate.
- * → va testa daca simbolul este prezent de zero ori sau de mai multe ori
- + → va testa daca simbolul este prezent o data sau de mai multe ori
- ? → va testa daca simbolul este prezent de zero ori sau o singura data
Daca stai si te gandesti putin probabil ca rolul caracterului asterisk ti se va putea parea ciudat si te vei intreba la ce il vei putea folosi. Gandeste-te la un cuvant in care ultima litera este optionala si daca este prezenta atunci va potrivi acel cuvant din text. Pana in acel moment acel cuvant din text inseamna ceva. Hai sa va arat un exemplu.
/apples*/ // va potrivi apple, apples, appless, applesss s.a.m.d
Importanta caracterului asterisk devine evidenta in momentul cand vrem sa potrivim acest tip de cuvant sau daca un metacaracter este prezent de zero ori sau de foarte multe ori. Sa presupunem ca avem un document html in care un cod este identat incorect iar tasta tab (reprezentat in expresiile regulate de metacaracterul \t) a fost apasata de prea multe ori iar codul nu arata cum ar trebui. Acesta este un alt exemplu de care trebuie sa tineti cont in folosirea caracterului asterisk.
Caracterul special plus este cel mai folosit dintre toate trei si asta pentru ca de cele mai multe ori vom dori sa potrivim un cuvant din text ce contine unul sau mai multe litere (simboluri in expresia regulata). Dupa cum puteti vedea diferenta dintre caracterele asterisk si plus este ca in cazul caracterului plus acel simbol trebuie sa fie prezent si daca vom testa exemplul anterior vom observa ca va potrivi doar cuvintele ce contin obligatoriu litera s la sfarsit, indiferent de cate ori se repeta.
/apples+/ // va potrivi apples, appless, applesss s.a.m.d
Caracterul special reprezentat de semnul intrebarii este cel care ne va spune ca un simbol poate fi prezent sau nu si daca este prezent acesta nu poate fi repetat. Acum sa testam exemplul anterior.
/apples?/ // va potrivi apple si apples
Este evidenta diferenta dintre aceste caractere si depinde doar de noi unde si cum le vom folosi. Ce trebuie sa retineti la caracterul →?← este ca acesta il vom mai intalni pentru ca impreuna cu alte caractere speciale va avea diferite roluri.
Rolul acoladelor in expresiile regulate
Anterior v-am spus despre rolul caracterelor speciale →* + ?← in expresiile regulate. Am vazut ca doua dintre acestea vor ajuta la potrivirea unor simboluri ce contin la sfarsit un simbol ce poate fi prezent de zero ori (*) sau de nenumarate ori sau de unu ori (+) sau de nenumarate ori.
Putem face mai mult de atat in expresiile regulate mai ales daca vrem sa potrivim un cuvant din textul tinta ce va avea un numar minim si un numar maxim de litere si pentru asta vom avea nevoie de acolade. In expresiile regulate i se mai spune si repetitie cuantificata.
In interiorul acoladelor vom avea una sau doua valori reprezentate obligatoriu de cifre. In cazul in care sunt prezente doua valori atunci acestea trebuie sa fie separate obligatoriu doar de virgula, orice alt caracter va face inutila expresia regulata. Aceste cifre vor reprezenta valori minime si maxime (in aceasta ordine) ale textului pe care vrem sa il potrivim cu ajutorul expresiei regulate.
Practic ceea ce ii spunem interpretorului de expresii regulate este sa potriveasca un text ce contine un numar minim si un numar maxim de caractere.
/\w{4,10}/ // va potrivi orice cuvant ce are minim 4 si maxim 10 caractere reprezentate de litere, cifre sau semnul sublinie (en. underscore)
In cazul in care valoarea maxima nu este definita iar virgula este prezenta atunci interpretorul de expresie regulata ii va atribui acesteia valoarea de infinit si in acest caz va potrivi orice text ce are minim 4 caractere reprezentate de litere, cifre sau semnul sublinie (en. underscore).
Spuneam la inceput ca in interiorul acoladelor vom putea avea si o singura valoare. Aceasta valoare devine automat o valoare fixa, o valoare ce va potrivi anumite caractere din textul tinta ce contine un anumit numar de litere sau ce vrem sa potrivim.
/\w{4}/ // va potrivi Java si Scri separat daca textul tinta este JavaScript
Calitatii ale expresiilor regulate
Expresiile regulate au cateva calitatii foarte importante iar trei dintre acestea sunt ca expresiile regulate sunt dornice (eager) sa iti returneze un rezultat, lacome (greedy) in a potrivi cat mai mult din textul tinta si lenese (lazy) in a potrivi cat mai putin. Vom testa aceste calitati prin folosirea caracterelor speciale →* + ?←, caractere ce vor fi aplicate diferit fata de ce v-am prezentat si spus pana acum.
Expresiile regulate sunt lacome
Veti vedea mai jos cum caracterul special →+← ajuta metacaracterul →\w← sa potriveasca unul sau mai multe caractere litera (word = letter, number or underscore). Acum este timpul sa il folosim si in alt scop si impreuna cu caracterul special ce reprezinta semnul intrebarii →?←.
Acum este foarte important sa ne amintim exact ce reprezinta aceste caractere luate separat. Am spus despre caracterul →+← ca va potrivi unul sau mai multe caractere si apoi am spus despre caracterul →?← ca va potrivi zero aparitii ale unui simbol sau unu aparitii ale unui simbol in textul tinta.
Testarea expresiilor regulate pentru a vedea cat sunt de lacome este prin adaugarea caracterului ce reprezinta semnul intrebarii dupa caracterul plus. Practic ii vom spune interpretorului de expresie regulata sa gaseasca si sa potriveasca cat mai mult si cu ajutorul caracterului special plus vom potrivi un caracter iar caracterul semnul intrebarii il va opri sa potriveasca mai departe. Urmatoarele exemple vor returna acelasi rezultat.
/Java?/ // va potrivi Java din Java singur sau JavaScript
/Java+?/ // va potrivi Java din Java singur sau JavaScript
Calitatea de expresie regulata lenesa
La fel ca si la calitatea anterioara, testarea calitatii de expresie regulata lenesa se va face si cu ajutorul caracterului ce reprezinta semnul intrebarii iar inaintea lui nu vom mai avea caracterul special plus ci caracterul special asterisk. Va mai amintiti ca acest caracter va testa daca un simbol va avea zero aparitii sau nenumarate aparitii. Amintiti-va inca odata ce rol are si caracterul special semnul intrebarii pentru ca este foarte important.
Ceea ce ii vom spune interpretorului de expresii regulate este sa gaseasca si sa potriveasca caracterele din textul tinta ce sunt prezente pana la penultimul pentru ca ultimul poate avea zero aparitii si este optional.
/Java*?/ // va potrivi Jav din Java singur sau JavaScript
Calitatea de dornica sa returneze un rezultat
Am vazut anterior cum calitatile de expresii regulate lacome si expresii regulate lenese sunt puse in aplicare in codurile noastre. Aceste doua calitati ne spun indirect ca expresiile regulate sunt nerabdatoare sa returneze un rezultat oricare este acela.
Gruparea si alternarea expresiilor
Gruparea metacaracterelor
Gruparea metacaracterelor in expresiile regulate se face cu ajutorul parantezelor rotunde. Totodata putem adauga in interiorul parantezelor rotunde si simboluri sau mai bine zis acele litere care nu au o semnificatie deosebita in expresiile regulate.
Motivele pentru care am dori sa folosim aceste paranteze rotunde si cu care vom grupa diferite simboluri sunt dintre cele mai diverse. Cel mai simplu motiv este acela ca in acest fel vom putea scrie o expresie regulata pe care o vom putea interpreta vizual mult mai usor. Un alt motiv este acela ca vom putea scrie mai putin intr-o expresie regulata.
/(abc){1,3}/ // va potrivi abc, abcabc si abcabcabc
Un motiv la fel de important este acela daca vrem sa potrivim un cuvant din textul tinta care are inaintea lui anumite litere.
/(in)?dependent/ // va potrivi independent si dependent
Observati ca semnul intrebarii ne ajuta in acest caz sa folosim mai mult de un simbol. Practic intreg setul de simboluri devine optional cu ajutorul caracterului special →?← si in acest fel va potrivi ambele cuvinte.
Este foarte important sa retineti ca in momentul cand interpretorul de expresii regulate detecteaza paranteze rotunde ce contin simboluri, acesta va memora ceea ce gaseste in interiorul acestora pentru a le folosi mai tarziu pentru a putea cauta inapoi in text. Aceasta modalitate poarta denumirea de backreferences despre care vom vorbi mai tarziu.
Alternarea metacaracterelor
Expresiile regulate ne permit sa folosim si mai mult rolul pe care il au parantezele rotunde. Am vazut ca putem grupa in interiorul lor mai multe simboluri ce pot fi folosite sa potriveasca un cuvant din textul tinta daca contine sau nu aceste simboluri.
Putem sa testam cu ajutorul parantezelor rotunde daca un cuvant din textul tinta are sau nu mai mult de un singur set de simboluri si pentru asta vom avea nevoie sa separam cumva aceste seturi de simboluri.
Modalitatea prin care vom putea separa aceste seturi de simboluri in interiorul parantezelor rotunde este prin folosirea caracterului special →|←. Acesta il veti gasi cel mai probabil pe aceeasi tasta cu backslash.
Este important sa retineti ca in momentul cand vom folosi acest caracter special, interpretorul de expresii regulate va testa daca acel text sau cuvant din textul tinta va avea unul dintre aceste seturi de simboluri prezente in interiorul parantezelor rotunde si separate de acest caracter.
In cazul in care reuseste sa potriveasca un set de simboluri dintre paranteze munca acestuia s-a terminat si nu il mai intereseaza sa testeze urmatorul set de caractere. Interpretorul de expresii regulate va testa de la stanga la dreapta fiecare set de simboluri si imediat ce reuseste sa potriveasca isi termina treaba si asa cum spuneam nu il mai intereseaza restul de seturi de simboluri.
/(I|A)nna/ // va potrivi Anna daca Inna nu exista
Daca in textul tinta vom avea ambele reprezentari ale cuvantului atunci interpretorul le va potrivi pe amandoua.
/(I|A)nna/ // va potrivi Inna si Anna daca ambele cuvinte exista
Cum sa scrii logic si eficient alternari
Am repetat mai sus cateva calitati ale expresiilor regulate iar acum vreau sa subliniez una dintre ele. Este vorba despre calitatea de dornica (eager) sa returneze un rezultat.
In momentul cand folosim alternari in expresiile regulate, interpretorul va compara de la stanga la dreapta seturile noastre de simboluri delimitate de caracterul →|←. Interpretorul va testa primul set de simboluri cu primele caractere (litere, cifre sau ce vrem sa testam) din textul nostru tinta si daca resuseste sa potriveasca aceste prime caractere ne va returna un rezultat cu ce a potrivit. Interpretorul nu va merge mai departe sa testeze si restul de simboluri din acel grup. Interpretorul ne va returna un rezultat cu ce a potrivit din setul de simboluri din acel grup si asta pentru ca rolul caracterului →|← este sa verifice daca una dintre optiunii reuseste sa potriveasca ceva din textul tinta si imediat ce reuseste ne returneaza un rezultat cu ce a potrivit.
Din acest motiv este bine sa scriem expresiile noastre regulate dupa modelul ca daca vrem sa potrivim ceva imediat cu scopul de a testa diferite probleme din textul tinta atunci este bine sa punem aceste tipuri de alternari la inceput si in acest fel vom scade timpul de executie al expresiei regulate prin modul ca nu va mai trebui sa caute in restul de text tinta daca alternarile noastre erau undeva pe la sfarsitul expresiei regulate. Ideea este in felul urmator. Daca este sa crape ceva atunci sa crape la inceput.
Expresii ancorate
Ancore de inceput si de sfarsit
Am repetat de mai multe ori in acest tutorial ca in expresiile regulate avem cateva caractere speciale ce vor avea mai mult de un singur rol. In aceasta sectiune vom face cunostinta si cu un alt caracter special care are un rol bine definit. Este vorba despre caracterul →$←.
Am vazut mai sus ca rolul caracterului →^← este unul de negatie in interiorul parantezelor drepte. Acum este timpul sa vedem si alt rol pe care acest caracter il are in expresia regulata.
Ancore de inceput
In expresiile regulate putem sa testam daca un text incepe cu un anumit set de litere si vom face asta cu ajutorul caracterului →^←. Acesta are rolul de ancora de inceput si va fi adaugat intotdeauna la inceputul expresiei regulate. In acest fel vom testa daca un text incepe cu diferite simboluri. Aceste simboluri, chiar daca se vor regasi apoi in text nu vor conta pentru ca rolul acestui caracter in aceasta parte din expresia regulata este de a testa daca textul respectiv incepe exclusiv cu aceste simboluri.
/^Anna/ // va potrivi primul cuvant Anna daca textul este → Anna este prietena cu Anna
Ancore de sfarsit
Opusul la ce ati vazut mai sus este reprezentat prin folosirea caracterului special →$←. Acesta va fi adaugat obligatoriu la sfarsitul expresiei regulate si va testa daca un text se termina intr-un anumit text.
/Anna$/ // va potrivi ultimul cuvant Anna daca textul este → Anna este prietena cu Anna
Folosirea acestor caractere
Am vazut cum pot fi potrivite diferite caractere din text la inceputul si la sfarsitul unui text tinta cu ajutorul acestor caractere speciale. Retineti ca pentru acest rezultat este obligatoriu ca acestea sa fie adaugate la inceputul (caracterul ^) expresiei regulate sau la sfarsitul (caracterul $) expresiei regulate.
In munca de programare vom putea testa daca un text va incepe cu diferite simboluri si se termina cu alte simboluri, dupa cum se poate vedea in urmatorul exemplu.
/^Anna.+Anna$/ // va potrivi intreg textul → Anna nu o cunoaste pe prietena Mariei pe care o cheama tot Anna
Este foarte important sa folositi modul multiline si sa rulati acest gen de expresie regulata in acest mod, in caz contrar aceasta expresie regulata va potrivi un text ce este compus doar dintr-un singur rand. Este evident si de ce potriveste un text ce contine acest text si se afla pe un singur rand. Va amintiti ca am spus mai sus ca in momentul cand apasam tasta ENTER editorul de text adauga in acel loc un caracter invizibil de linie noua sau rand nou →\n← si din acest motiv expresia regulata nu va mai potrivi intreg textul cu ajutorul simbolurilor si caracterelor speciale pentru ca randul nostru se termina in acest caracter invizibil.
Potrivirea cuvintelor
Expresiile regulate vin in intampinarea noastra a programatorilor cu doua metacaractere pe care le-am vazut un pic mai sus intr-o lista si despre care nu am spus mai nimic. Aceste metacaractere sunt →\b← si →\B←.
Metacaracterul →\b← are ca unic scop potrivirea unui grup de caractere ce pot fi alcatuite din litere, cifre si/sau semnul sublinie (en. underscore). Va amintiti ca semnul sublinie (en. underscore) este interpretat ca fiind litera desi este caracter special. Mai pe scurt acest metacaracter va potrivi un cuvant sau mai multe. Aceste cuvinte pot fi alcatuite din litere si/sau cifre si/sau semnul sublinie (en. underscore).
Metacaracterul →\b← devine foarte util in situatia in care ne dorim sa facem expresia regulata mai usor de interpretat si sa potrivim mai usor un cuvant indiferent de cate caractere contine iar urmatoarele exemple vor potrivi acelasi text.
/\w+/g → va potrivi orice cuvant
/\b\w+\b/g → va potrivi orice cuvant
Folosirea metacaracterului →\b← ne va ajuta sa crestem viteza de executie a expresiei regulate si asta pentru ca prin folosirea lui ii vom spune interpretorului sa ne potriveasca doar cuvinte sau limite de cuvinte (word boudaries).
Metacaracterul →\B← reprezinta opusul metacaracterului →\b← si va potrivi un grup de caractere ce nu este limita de cuvant (ex. interiorul unui cuvant) si uneori diferite caractere speciale.
/\B\w\B/ // va potrivi a si v daca textul tinta este Java
\B.+\B → va potrivi litera n din textul Ana, cifra 2 din textul 123 sau chiar $%# daca este intalnit asa
Grupuri de capturare si referinte catre inapoi in text
Referinte catre inapoi in text
Va amintiti cand am spus mai sus despre modul cum putem grupa simboluri in expresiile regulate si ca vom face asta cu ajutorul parantezelor rotunde. Tot in acea sectiune v-am spus si ca interpretorul de expresii regulate va memora ce a potrivit cu ajutorul parantezelor rotunde pentru a putea folosi mai tarziu aceasta informatie. Aceasta tehnica mai poarta denumirea si de backreferences si desprea ea vom vorbi acum.
/h(t{2})p/ // potriveste http si retine pentru mai tarziu tt
In exemplul anterior vedeti cum functioneaza si cum interpretorul retine informatia dintre paranteze pentru a o folosi mai tarziu. Acest lucru se face automat si reprezinta o setare de baza in expresiile regulate. Nu tine cont de modul si motivele in care iti scrii expresiile regulate.
Aceasta informatie stocata de interpretor va trebui intr-un fel accesata de noi si pentru a putea accesa aceasta informatie ne vom folosi de backreferences sau referinte catre inapoi in text.
Modalitatea prin care vom accesa aceasta informatie se face cu ajutorul caracterului special →\← si urmat de un numar de la 1 la 9. Diferite interpretoare de expresii regulate iti permit sa folosesti numere de la 10 la 99 si este important sa retineti aceasta informatie daca lucrati cu expresii regulate in alte limbaje de programare. Totodata diferite interpretoare de expresii regulate vor folosi in locul caracterului →\← caracterul →$←.
/(jsdev) to \1/ // va potrivi jsdev to jsdev
Observati cum putem sa folosim referintele catre inapoi in text si sa accesam astfel informatia care este stocata cu ajutorul parantezelor sau grupari simbolurilor. Cifra ce face parte din referintele catre inapoi in text (backreferences) va face referire la a cata stocare de informatie ne referim si dupa cum am vazut in subsectiunea alternarea metacaracterelor vom putea potrivi mai multe grupuri de simboluri.
/(mar)(par)(gutui)\3\2\1/ // va potrivi marpargutuigutuiparmar
Acest exemplu are putine sanse sa fie folosit de catre voi dar principalul scop este sa intelegeti cum functioneaza referintele catre inapoi in text. Urmatorul exemplu are mai multe sanse sa fie folosit mai ales daca vom dori sa potrivim etichete HTML si continutul acestora.
/<(strong|em)>.+?<\/\1>/ // va potrivi <strong>Text</strong> si <em>Text</em>
Observati ca am folosit caractere →+?← impreuna pentru a-i spune interpretorului sa nu fie lacom si sa potriveasca doar minimul necesar.
Folosirea caracterului special semnul intrebarii in referinte catre inapoi
Pana acum ati vazut si retinut rolul caracterului special →?←. Acum este timpul sa vedeti cum acesta va poate pacali daca nu sunteti atenti in momentul cand il folositi in referinte catre inapoi (backreferences).
Stim ca prezenta caracterului special →?← ne va spune ca acel element sau grup de simboluri este optional si poate fi prezent de zero ori sau unu ori.
/(a)?b/ // va potrivi ab si b
Ce facem daca vom adauga caracterul →?← si vom face astfel optional un simbol din grup si in acest caz vom avea un singur simbol in grup.
/(a?)b/ // va potrivi ab si b
Observati ce se intampla ? Spuneam mai sus ca rolul parantezelor rotunde este de a grupa simboluri si/sau metacaractere. Totodata cand interpretorul de expresii regulate intalneste paranteze rotunde, acesta va memora ce a potrivit cu ajutorul acestor paranteze rotunde. In primul caz am avut intreg grupul optional si putea fi prezent de zero ori sau de unu ori, dupa cum ne spunea caracterul semnul intrebarii, iar in exemplul doi doar simbolul din interiorul grupului este optional iar acesta poate fi prezent de zero ori sau unu ori si din acest motiv interpretorul va retine cu ajutorul parantezelor un string cu lungimea de zero caractere. Pare putin ciudat dar interpretorul va potrivi caracterul b singur.
In JavaScript acest caz functioneaza dar in alte expresii regulate din alte limbaje de programare s-ar putea sa nu potriveasca litera b interpretorul din acel limbaj de programare si asta pentru ca sunt parerii ale specialistilor care nu au ajuns la un consens comun pentru a rezolva aceasta problema.
Cauta si inlocuieste intr-un fisier cu ajutorul referintelor catre inapoi
Putem folosi referintele catre inapoi (backreferences) in editoarele de text ce suporta expresiile regulate. Editorul de text care suporta foarte bine expresiile regulate este Sublime Text 2. Voi folosi aceasta versiune pentru a va oferi urmatorul exemplu prin care voi rearanja continutul text (ce va este aratat imediat) cu ajutorul referintelor catre inapoi.
Sa presupunem ca avem urmatorul text caruia vrem sa ii inversam numele cu prenumele.
1.Ioana Maria, Manager Senior2.Alexandra Popescu, Entry Level Manager
3.Vali Valentin, Frontend Engineer
Mai intai trebuie sa ne asiguram ca avem editorul text Sublime Text 2 deschis si in interior acest text adaugat intr-un fisier cu extensia .txt
Urmatorul pas este sa dam click pe Find din meniul orizontal al editorului si apoi vom alege optiunea Replace din meniul aparut. Sau mai simplu vom apasa tastele Ctrl + H.
In optiunea de deasupra (Find) vom scrie urmatoarea expresie regulata ce va selecta prima cifra cu caracterul punct, apoi va selecta numele plus spatiul dintre cuvinte si in final va selecta prenumele. Toate acestea le vom grupa pentru a le utiliza imediat.
(\d\.)(\b\w+\b )(\b\w+\b)
In optiunea Replace vom adauga urmatorul cod ce va face referire la referinte catre inapoi. Editorul de text Sublime Text 2 suporta versiunea prin care in locul backslash-ului si vom adauga caracterul →$← si in final cifra aferenta grupului memorat de interpretor.
$1 $3 $2
Putem mai departe sa dam click pe Replace pentru a vedea cum sunt inversate numele cu prenumele sau Replace All pentru a le inlocui pe toate si sa terminam treaba.
Acest exemplu este foarte util si merita sa il retineti pentru situatia in care aveti un fisier .csv, .txt sau orice alt fisier text ce va contine sute sau mii de randuri cu nume, prenume, data nasteri, functia ocupata s.a.m.d. sau orice altceva si vreti sa inversati, inlocui date etc. foarte rapid scutindu-va de munca pe cateva ore sau zile.
Anularea optiunii de referinte catre inapoi
In expresiile regulate avem posibilitatea de a anula referintele catre inapoi si pentru asta vom face cunostinta cu un alt caracter special dar si cu un alt rol pe care il are semnul intrebari. Aceasta tehnica mai poarta denumirea si de non-capturing group.
Modalitatea prin care vom anula referintele catre inapoi o vom face prin adaugarea la inceputul grupului de simboluri ori metacaractere a semnului intrebarii urmat imediat de caracterul doua puncte →?:←.
Semnul intrebarii va avea rolul de a-i spune interpretorului ca acest grup de caractere are un alt rol iar caracterul doua puncte ii va spune ca rolul grupului este de a nu memora ce se potriveste cu acest grup.
Este vital sa retineti aceasta fraza si pentru rolul pe care il are semnul intrebari pentru ca il vom revedea imediat cu un alt scop.
Potrivirea unor caractere daca alte caractere sunt sau nu prezente
Expresiile regulate ne ofera posibilitatea sa potrivim diferite caractere din textul tinta daca alte caractere sunt prezente sau nu. Aceasta tehnica mai poarta denumirea si de lookaround assertions. Lookaround assertions este impartit in doua categorii cu denumirea generica de pozitiv si negativ cu referire directa la caracterele prezente si la caracterele care nu sunt prezente, in aceasta ordine. Lookaround assertions are rolul de a potrivi un set de caractere daca alte caractere sunt prezente iar aceste caractere nu vor face parte din ce a potrivit. Lookaround assertions are lungimea de zero caractere.
Potrivirea unor caractere daca alte caractere sunt prezente
Sintaxa pozitiv lookaround assertions
Sintaxa pentru pozitiv lookaround assertions este una foarte simpla si asemanatoare cu non-capturing group iar diferenta este ca in locul caracterului doua puncte vom avea semnul egal pentru pozitiv urmat apoi de caracterul dupa care vom face referinta sau restul de expresie regulata.
/\w(?=,)/ // va potrivi un caracter litera, cifra sau semnul sublinie (en. underscore) care are imediat langa el o virgula
Potrivirea unor caractere daca alte caractere nu sunt prezente
Sintaxa negativ lookaround assertions
Sintaxa pentru negativ lookaround assertions este aproape identica cu pozitiv lookaround assertions iar diferenta este ca in locul caracterului egal vom avea semnul mirarii.
/\w(?!,)/ // va potrivi un caracter litera, cifra sau semnul sublinie (en. underscore) care nu are imediat langa el o virgula
Important la lookaround assertions
Lookaround assertions ne permite sa facem mai multe decat v-am aratat pana acum. Putem sa facem mai multe teste pe acelasi set de caractere si sa vedem daca un anumit set de cifre intruneste diferite formate. Sa presupunem ca avem urmatoarele randuri cu numere de telefon si vrem sa potrivim un numar de telefon sau mai multe daca intruneste toate cerintele noastre.
- 555-782-5699
- 555-231-3210
- 555-450-1234
Sa zicem ca vrem sa potrivim un numar ce contine cifrele de la 0 la 5 separate de caracterul minus si este in formatul de trei cifre pe primele seturi si la sfarsit un set format din patru cifre. Cu urmatoare expresie regulata vom potrivi al doilea si al treilea numar.
/(?=^[0-5\-]+$)\d{3}-\d{3}-\d{4}/m
Practic i-am spus interpretorului de expresie regulata sa caute in modul multiline un set de cifre care de la inceput si pana la sfarsit va trebui sa contina cifrele de la zero la cinci, separate de caracterul minus si in formatul de trei cifre urmate de caracterul minus, apoi alte trei cifre urmate si acestea de caracterul minus si in final un set de patru cifre.
Acum este timpul sa adaugam inca un nivel in expresia regulata si vrem sa testam daca acel numar se termina cu 3210 pentru ca vrem sa potrivim numarul din mijloc. Urmatoarea expresie regulata este mai mult decat intuitiva si va potrivi numarul de telefon din mijloc.
/(?=^[0-5\-]+$)(?=.+3210)\d{3}-\d{3}-\d{4}/m
Ce am vrut sa va arat cu aceste exemple a fost sa va arat ca folosind lookaround assertions puteti sa testati in mai multe feluri in aceasi expresie regulata un set de caractere si prin aceasta modalitate sa scrieti mai putin cod dar mult mai inteligent.
Desi nu v-am spus pana acum, lookaround assertions a cautat daca diferite caractere din textul tinta sunt prezente dupa caracterele pe care vrem sa le potrivim iar aceasta tehnica poarta denumirea de lookahead assertions. Exista si opusul acesteia si poarta denumirea de lookbehind assertions si din pacate nu este suportata de limbajul JavaScript, Ruby 1.8 si UNIX dar este suportat de limbajele .NET, Java, Perl, PHP, Python si de la Ruby 1.9.
Lookbehind assertions functioneaza aproape identic cu lookahead assertion iar diferenta este data de sintaxa (ce are intre caracterul semnul intrebarii si egal sau semnul mirarii caracterul mai mic sau mai mare →<←) si de diferite limbaje care accepta sau nu rolul caracterelor →*← si →+←.
Cateva exemple de tinut minte
La sfarsitul acestei sectiuni, va arat cateva exemple pe care este bine sa le retineti pentru ca va pot fi de mare folos.
- /(Java)(?!.+\1)/ // va potrivi ultima prezenta a cuvantului Java dintr-un text
- /<(em|strong)>.*?<\/\1>/ // va potrivi o eticheta html din grup cu tot continutul din interior - non lacom
- ^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ // va potrivi orice adresa IP de la 0.0.0.0 la 255.255.255.255
/(Java)(?!.+\1)/ /<(em|strong)>.*?<\/\1>/ ^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
JavaScript API
API reprezinta o abreviere de la Application Programming Interface. In limbajul JavaScript avem incluse foarte multe API-uri iar mai jos sunt prezentate cateva.
HTML5 File API
HTML5 File API permite limbajului JavaScript sa incarce si sa proceseze fisiere de pe sistemul local de operare in paginile HTML5. Din motive de securitate, in acest proces trebuie sa "participe" si utilizatorul paginei.
Nucleul HTML5 File API
La baza HTML5 File API sunt cateva functii iar acestea sunt prezentate in lista urmatoare. Toate aceste functii pot fi instantiate si prin instantiere, fiecare variabila-obiect reprezinta ceva anume.
- FileList - prin instantiere reprezinta o lista cu fisiere (exemplu: continutul unui director)
- File - prin instantiere reprezinta un fisier
- Blob (Binary Large OBject) - prin instantiere reprezinta continutul unui fisier
- FileReader - prin instantiere reprezinta posibilitatea de a "citi" intr-un anume mod un fisier (functiile readAsArrayBuffer(), readAsBinaryString(), readAsDataURL(), readAsText()) si de a executa o functie anonima la un anumit eveniment, functie ce va primii un argument cu date despre fisierul citit
Membrii obiectului (metode si / sau proprietatii) FileList.
Membrii obiectului (metode si / sau proprietatii) File.
Membrii obiectului (metode si / sau proprietatii) Blob.
Membrii obiectului (metode si / sau proprietatii) FileReader.
Proprietatii importante ale obiectului ce instantiaza functia FileReader
- readyState
- valoarea este 0 (EMPTY) - nici-un bit de informatie din fisier nu a fost incarcat
- valoarea este 1 (LOADING) - continutul fisierului este in proces de incarcare
- valoarea este 2 (DONE) - continutul fisierului este incarcat complet
- result - proprietate ce contine continutul fisierului ce a fost citit
-
error
- NOT_FOUND_ERROR(1) - fisierul sau Blob nu a putut fi gasit in timpul citirii
- SECURITY_ERR(2) - browserul a detectat o problema de securitate
- ABORT_ERR(3) - citirea fisierului a fost oprita de utilizator ori de sistemul de operare
- NOT_READABLE_ERR(4) - browserul a detectat o problema de securitate
- ENCODING_ERR(5) - citirea fisierului a fost oprita de utilizator ori de sistemul de operare
Metode importante ale obiectului ce instantiaza functia FileReader
- readAsText - citeste fisierul ca un fisier de tip text (folosit pe fisiere de tip text)
- readAsDataURL - returneaza codarea fisierului in formatul data://URL (folosit pe fisiere de tip imagine, datele sunt in baza 64)
- readAsArrayBuffer - citeste fisierul si returneaza un obiect matrice cu octeti
- readAsBinaryString - citeste fisierul si returneaza un sir cu date binare (0...255)
Evenimente importante ale obiectului ce instantiaza functia FileReader
- progress - eveniment declansat in timpul citirii/incarcarii fisierului
- loadend - eveniment declansat in momentul cand fisierul a fost citit/incarcat
Exemplu complet de incarcare si citire a unui fisier
<style> div{ width: 286px; max-height: 400px; padding: 5px; margin-bottom: 5px; overflow-y: auto; border: 2px solid black; } div img{ width: 100%; } </style> <div> <h3>Incarca o imagine</h3> <input type="file" id="img"> </div> <div id="imagine-incarcata"></div> <div> <h3>Incarca continutul unui fisier</h3> <input type="file" id="file"> </div> <div id="continut-fisier"></div> <script> function incarcaFisier(inputFileId, divTargetContent){ let input = document.querySelector(inputFileId), div = document.querySelector(divTargetContent), reader = new FileReader, dimensiuneFisier = (256*8) * 1024; if( inputFileId == "#img" && divTargetContent == "#imagine-incarcata" ){ input.addEventListener("input", function(){ let files = this.files; if(files.length == 1){ // evenimentul loadend reader.addEventListener("loadend", (e)=>{ // "goleste" containerul div.innerHTML = ""; if(e.loaded < dimensiuneFisier){ let img = document.createElement("img"); img.src = e.target.result; div.appendChild(img); } else{ alert("Dimensiunea fisierului este prea mare."); } }); reader.readAsDataURL(files[0]); } }); } if( inputFileId == "#file" && divTargetContent == "#continut-fisier" ){ input.addEventListener("input", function(e){ let files = this.files; if(files.length == 1){ // evenimentul progress // incarca un fisier cu dimensiunea putin mai mare reader.addEventListener("progress", (e)=>{ if(e.lengthComputable){ let fileLoaded = Math.round((e.loaded / e.total) * 100); console.log(`Fisierul este incarcat ${fileLoaded}%`); } // console.log(e); // console.log(e.target); // console.log(e.target.error); // console.log(e.target.error.code); }); // evenimentul loadend reader.addEventListener("loadend", (e)=>{ if(e.loaded < dimensiuneFisier){ div.innerHTML = e.target.result; } else{ alert("Dimensiunea fisierului este prea mare."); } }); reader.readAsText(e.target.files[0]); } }); } } incarcaFisier("#img", "#imagine-incarcata"); incarcaFisier("#file", "#continut-fisier"); </script>
Vibration API
Vibration API permite programatorilor de limbaj JavaScript sa declanseze functia de vibrare a dispozitivului daca acesta exista pe dispozitiv. Nucleul acestui API este reprezentat de metoda vibrate(), metoda ce apartine obiectului navigator care la randul lui apartine obiectului window.
Membrii obiectului (metode si / sau proprietatii) navigator.
Metoda vibrate
Aceasta metoda poate avea un singur parametru ce poate fi reprezentat astfel:
-
o valoare numerica naturala pozitiva
- daca valoarea este cifra zero, functia de vibrare opreste dispozitivul sa mai vibreze
- daca valoarea este mai mare de zero, aceasta valoare reprezinta timpul de vibrare al dispozitivului o singura data - valoarea numerica exprimata reprezinta timpul in milisecunde cat dispozitivul ar trebui sa vibreze
- o matrice cu valori numerice pozitive unde indexul par (0,2,4,6...) reprezinta timpul in milisecunde de vibrare al dispozitivului iar indexul impar (1,3,5...) reprezinta timpul in milisecunde in care dispozitivul sa nu vibreze
Exemplu de folosit pe un dispozitiv mobil
<meta name="viewport" content="width=device-width, initial-scale=1"> <button id="start-vibrate">Start vibreaza telefon</button> <br /> <br /> <button id="stop-vibrate">Stop vibreaza telefon</button> <script> let vibrate = { interval: null, start(sablonVibrare, time = 3000){ this.interval = setInterval(()=>{ navigator.vibrate(sablonVibrare); }, time); }, stop(){ clearInterval(this.interval); navigator.vibrate(0); } }, startBtn = document.querySelector("#start-vibrate"), stopBtn = document.querySelector("#stop-vibrate"); startBtn.addEventListener("click",()=>{ vibrate.start([700,500,700]); }); stopBtn.addEventListener("click",()=>{ vibrate.stop(); }); </script>
Notification API
Notification API permite programatorilor de limbaj JavaScript sa afiseze utilizatorului paginii web diferite mesaje scurte de tip text sub forma unei notificari. Mesajele text sunt afisate utilizatorului intr-o "casuta" mica pe o grafica independenta de programator si de obicei in coltul din dreapta jos.
Din motive de securitate dar si de a oferi utilizatorului o experienta placuta in pagina web, acesta din urma trebuie sa fie implicat in acest proces iar pagina web trebuie sa fie "oferita" utilizatorului intr-un context securizat (protocolul HTTP si certificatul SSL rezultand HTTPS). In caz contrar, Notification API nu va functiona corespunzator.
Nucleul acestui API
La baza acestui API se afla functia Notification, functie ce apartine obiectului window. In versiunile moderne ale browserelor functia Notification returneaza un Promise.
Membrii obiectului (metode si / sau proprietatii) Notification.
Metoda requestPermission() si proprietatea permission
In procesul de a trimite notificari utilizatorului paginii web, acesta din urma trebuie sa fie implicat. Pentru a trimite notificari, trebuie sa il intrebam. Vom putea face acest lucru folosind metoda requestPermission(). Aceasta metoda seteaza proprietatea permission la una din valorile din lista urmatoare:
- default - valoare implicita, opinia utilizatorului este necunoscuta iar browserul se va comporta ca atunci cand opinia acestuia este denied
- denied - utilizatorul refuza sa primeasca notificari
- granted - utilizatorul doreste sa primeasca notificari
Instantiind functia Notification
Prin instantiere, apelam constructorul. Acestuia ii putem "transmite" doi parametrii si numai primul este obligatoriu. In lista urmatoare sunt prezentati tipul si datele acestor parametrii:
- sir de caractere ce reprezinta Titlul mesajului text pentru utilizator
-
un obiect JSON cu cheile
- body - valoare: sir de caractere ce reprezinta corpul mesajului text pentru utilizator
- icon - cheie optionala, valoare: sir de caractere ce reprezinta calea catre o imagine cu dimensiunea mica, va fi afisata in notificare
Exemplu simplu de notificare a unui utilizator
Este o buna practica sa verificam suportul acestui API in browser si pentru asta vom verifica "existenta" functiei Notification in obiectul window. Totodata, putem sa verificam si daca functia Notification returneaza un Promise. Aceste doua verificari le puteti include in scenariile voastre de cod.
function notificare(data){ if(!("Notification" in window)){ return "Error: Nu avem suport pentru acest API in browser."; } if(Notification.permission == "default"){ Notification.requestPermission(); } if(Notification.permission == "denied"){ let bool = confirm("Doriti in continuare sa nu primiti notificari ?"); if(bool){ return false; } else{ Notification.requestPermission(); } } if(Notification.permission == "granted"){ let n = new Notification(data[0],{ body: data[1], // corpul mesajului icon: data[2] // optional: link catre o imagine mica }); setTimeout(()=>{ n.close(); },3e3); } } notificare(["Titlu notificare", "Textul propriu-zis", ""]);
Web Storage API
Web Storage API ofera posibilitatea programatorilor de limbaj JavaScript sa salveze informatii pe calculatorul utilizatorului sub forma de cheie cu valoare. Aceste informatii au o durata de "viata" si pot fi sterse folosind una din urmatoarele situatii:
- utilizatorul paginii isi sterge istoricul si/sau informatiile aferente
- tab-ul din browser sau browserul insusi este inchis
- programatorul insusi foloseste la un moment dat in scenariul de cod metoda removeItem(cheie) pentru a sterge informatia asociata cheii - aceasta metoda se regaseste in ambele proprietati din lista urmatoare
Urmatoarele proprietati sunt membrii ai obiectului window si stau la baza acestui API.
- sessionStorage
- localStorage
Ambele proprietati au aceeasi membrii ce fac acelasi lucru pentru ca la baza fiecaruia este obiectul literar Storage. Desi fac acelasi lucru, folosirea uneia dintre aceste proprietati face ca durata de "viata" a informatiilor sa fie mai "scurta" sau mai "lunga".
Membrii obiectului (metode si / sau proprietatii) Storage.
Membrii obiectului (metode si / sau proprietatii) sessionStorage.
Membrii obiectului (metode si / sau proprietatii) localStorage.
Proprietatea sessionStorage
Aceasta proprietate ofera o durata de "viata" a informatiilor salvate atata timp cat sesiunea de lucru (tab-ul / browserul) este deschisa. Informatiile sunt pastrate chiar daca se reincarca / reimprospateaza pagina.
Proprietatea localStorage
Aceasta proprietate ofera o durata de "viata" a informatiilor salvate pana ce utilizatorul le sterge sau pana ce programatorul le sterge explicit.
Metodele setItem si getItem
Aceste metode sunt folosite pentru a "seta" si a "prelua" informatiile salvate. Metoda prin care "setam" va avea doi parametrii iar acestia sunt cheia si valoarea iar metoda prin care preluam va avea doar un singur parametru si va reprezenta cheia careia vrem sa ii aflam valoarea.
Observatie: Functia setItem salveaza valoarea la care face referire cheia sub forma de sir de caractere.
Alti membrii ai acestor proprietati cu roluri diferite
- length - daca valoarea este zero, nu sunt salvate informatii in browser, o valoare pozitiva diferita de zero reprezinta cate chei pastreaza valori
- removeItem - metoda cu un singur parametru ce este folosita pentru a sterge valoarea unei chei
Exemplu in care se foloseste proprietatea-obiect localStorage - (cea mai buna)
<!DOCTYPE html> <html> <head> <title></title> </head> <body> <input type="color" value="#ffffff" /> <script> function preferintaUtilizator(){ let body = document.body, input = document.querySelector("input"), cheie = "culoare", culoareSalvata = localStorage.getItem(cheie); if(culoareSalvata != null){ input.value = culoareSalvata; body.style.background = culoareSalvata; } // incercati si evenimentul change input.oninput = function(){ localStorage.setItem(cheie, this.value); body.style.background = this.value; } } preferintaUtilizator(); </script> </body> </html>