1.Mediul de executieAsa cum am mentionat mai sus, programele JavaScript nu solicita in maniera implicita existenta unor mecanisme de tip intrare/iesire (acestea pot fi diferite in functie de mediul de executie).
Pentru a putea fi rulat,in mod interpretat, codul JavaScript trebuie sa se bazeze pe un mediu de executie.
Acest mediu de executie, numit si mediu-gazda (host environment) poate fi oferit de:
- navigatorul Web (e.g. Firefox) - programele JavaScript vor fi incluse via elemental <script> ;
- o platforma de dezvoltare a aplicatiilor - de exemplu, Adobe Flex ;
- o aplicatie – vezi suportul disponibil in cadrul utilitarului UltraEdit sau a sulitei Adobe Photoshop ;
- un processor (engine) independent – de exemplu, Yahoo!Widget Engine;
- componente ale sistemului de operare (precum Dashboard pentru Mac OS X, Windows Scripting Host in Windows XP sau Sidebar pentru Windows Vista).
Unii specialisti considera ca JavaSCript nu prezinta doar caracteristici de limbaj obiectual, ci si de limbaj functional. Putem considera JavaSCript un limbaj prototopizat, dupa cum vom vedea in continuare.
2. O prezentare generala a limbajului La fel ca in cazul altor limbaje de programare, in JavaScript pot fi definite variabile (de diverse tipuri scalare sau compuse), pot fi folosite instructiuni (de atribuire,de test, de ciclare, de control), pot fi utilizate obiecte, continand metode (functii) predefinite sau descries de programator.
In continuare, vom preciza caracteristicile definitorii ale limbajului.
Considerente sintactice Din punct de vedere sintactic, limbajul utilize initial urmatoarele cuvinte-cheie rezervate
: break, else, new, var, case, finally, return, void, catch, for, switch, while, continue, function, this, with, default, if, throw, delete, in, try, do, instanceof, typeof. In present sunt
specificate si
abstract, enum, int, short, Boolean, export, interface, static, byte, extends, long, super, char, final, native, synchronized, class, float, package, throws, const, goto, private, transient, debugger, implements, protected, volatile, double, import, public. Acesti termini nu pot fi folositi sa desemneze identificatori de variabile sau nume de functii
. Limbajul JavaScript este
case-sensitive – majusculele au o semnificatie diferita fatza de literele mici.
Tipuri de dateStandardul ECMAScript precizeaza deja ce tipuri de date sunt predefinite.
Uzual, vom folosi :
- Number - orice valoare numerica va fi reprezentata in dubla precizie, fiind stocata pe 64 de biti ( nu exista valori intregi) ;
- String – face posibila existenta secventelor de caractere Unicode, fiecare fiind memorat pe 16 biti;
- Boolean – folosit pentru expresii care se pot evalua prin true sau false ;
- Object-desemneaza obiecte; in aceasta categorie intra si tipurile Function (functii/metode), Array (tablouri), Date (date calendaristice) sau RegExp (ecpresii regulate) ;
- Null – semnifica “nici o valoare” ;
- Underfined – precizeaza “nici o valoare afisata inca”.
Operatiile avansate cu numere pot fi realizate prin intermediul obiectului Math predefinit. Acesta pune la dispozitie:
- constante predefinite : Math.PI, Math.E, Math.LN10 etc. ;
- metode : Math.abs(x), Math.ceil(x), Math.cos(x), Math.exp(x), Math.floor(x), Math.log(x), Math.max(x, ..), Math.min(x, ..), Math.pow(x, y), Math.random( ), Math.round(x), Math.sin(x), Math.sqrt(x) si altele.
Pentru a converti un sir de caractere in numer, se ofera functia
parseInt ( ).Astfel,
parseInt (“123”) va intoarce valoarea numerica 123, iar
parseInt (“11”, 2) va avea drept rezultat 3, deoarece a fost precizata si baza de numeratie. Daca argumentul nu poate fi convertit in valoare numerica, vom obtine valoarea speciala NaN. Putem verifica daca o expresie are valoarea
NaN cu ajutorul functiei
isNaN ( ).
Trebuie acordata atentie sirurilor care incep cu cifra 0, pentru ca ele vor fi tratate ca valori octale. Astfel
, parseInt (“011”) va genera 9.Pentru a preintampina diverse comportamente “caudate” ale codului, este recomandat sa indicam intotdeauna baza de numeratie – astfel,
parseInt (“011”, 10) va intoarce rezultatul dorit.
Acestea pot fi experimentate apeland la o asa-zisa “consola JavaScript”, in care putem introduce direct linii de cod.
Alaturi de
parseInt ( ), mai putem folosi
parseFloat ( ) pentru a converti siruri in valori numerice in dubla precizie si
eval ( ), care are drept rol evaluarea oricarei expresii de tip sir de caractere.
Cu toate ca fiecare element al unui sir de caractere este reprezentat pe doi octeti (16 biti), un caracter reprezinta un sir de lungime1.
Pentru siruri, apartzinand desigur tipului String, vom utea recurge la urmatoarele metode predefinite
: s.charAt (pozitie) s.charCodeAt (pozitie) s.concat (s1, ..). s.indexOf (s1, start), s.match (expresie_regulata), s.replace (sir,sir_nou), s.slice (start, final), s.split ( separator, limita), s.substring (start, final), s.toLowerCase ( ), s.toUpperCase ( ) etc.
De exemplu,expresia
“web 2.0”.length va avea ca rezultat valoarea numerica 7-pot fi apelate in mod direct metode, pentru ca sirurile sunt obiecte. In ceea ce priveste tipul Boolean, trebuie precizat faptul ca valorile 0, “” (sirul vid) , NaN si null vor fi evaluate prin false. De exemplu, expresia !”” va intoarce valoarea logica true.
Variabilele In JavaScript, variabilele au asociati identificatori compusi din litere, cifre si semnul de subliniere (simbolul “_”), scopul lor fiind local sau global.
Variabilele trebuie declarate folosind cuvantul-cheie
var. Variabilele declarate fara valoare asignate sunt considerate
undefined.
Var marime;
Var numeAnimal= “Tux”;
Daca nu se foloseste
var, atunci variabila este considerate globala. Trebuie evitata o astfel de situatie, din cauza problemelor ce pot suveni ulterior (depanare dificila, conflict cu alte variabile avand acelasi nume, mentenanta precara a codului etc.).
Operatorii Limbajul JavaScript include operatorii uzuali aritmetici, relationari, logici, speciali (precum
typeof sau operatorul conditional
?:, familiar progrmatorilor in C) si de manipulare a obiectelor.
Pentru valorile numerice, putem folosi
+ , - , * , / . %. Acestia pot fi utilizati si in instructiunile de agisurare
: +=, -=, *=, /=, %=. De asemenea, putem recurge la operatorul de incrementare
++ si la cel de decrementare al valorilor numerice,
--. Conectarea sirurilor de caractere se realizeaza cu ajutorului operatorului
+ :”Web”+ “2.0”, va avea drept rezultatul sirul “Web 2.0”.
Conversia titpurilor de date se face “din zbor”. De exemplu expresia “3” + 4+ 5. va intoarce sirul “3 4 5”, iar 3+4+ “5” va genera “7 5”. Asadar, adaugand un sir vid la o expresie, o convertim pe aceasta la tipul
string.
Comparatiile se pot realize pe baza operatorilor relationari obisnuiti
< , > ,<=, >= care pot avea drept operanti valori numerici sau siruri de caractere. Dupa cum eram de asteptat, egalitatea se tasteaza cu
== si
!=.
Pentru a afla tipul unei expresii., folosim operatorul
typeof care va intoarce un sir de caractere desemnand tipul numele tipul de date determinte. De exemplu,
typeof ”Web” rezulta “String”, iar
typeof True ofera ca si rezultat sirul
“boolean”.
Desigur sunt diponibili si operatorii logici
&& si
|| dar si operatorul de test
?.
Instructiuni Pentru testarea, vom recurge la
if…else si/sau
switch.
Pentru
switch, sunt premise expresii la fiecare ramura
case-testare se realizeaza cu operatorul
= = =.Exemplu:
Switch (2+3):/*Sunt premise expresii */
Case 4+1 : egalitate();
Break;
Default : absurd ();|//nu se apeleaza niciodata!
Instructiunile de ciclare clasice sunt- si aici-
while, do….while si for/*Un exemplu pentru do…while */
Do {
Var nume= oferaNume();
} while (nume!=””);
/* Un exemplu pentru for */
For (var contor=0;contor<33;contor++);
{//de 33 ori…}
De asemenea se ofera suport pentru tratarea exceptiilor, similare mecanismului pus la disozitia de Java sau C#.
Forma generala binecunoscuta este:
try
{//linii “periculoae” care pot cauza exceptii
}
catch (eroare)
{//linii executate la apritia unor exceptii
}
finally
{//linii care se vor executa la final
}
Orice linii succeptibile de a cauza exceptii (de exemplu, date eronate primate de la utilizator, instintarea unui obiect, pierderea concetiunii HTTP etc.) vor fi plasate in blocul
try(). Aparitia unei sau a mai multor exceptii va determina interpretorul JavaScript sa “sara” direct la executia liniilor din bloc
catch().Desigur pentru “emite” propriile noastre exceptii prin intermediul constructiei
throw:Throw new error (“0 eroare de a noastra…”);
Instructiunile
try…catch pot fi imbricate dupa cum se poate remarca in exemplul de mai jos:
function try catch (arg){
try {
try {
If (arg = =””)//evalueaza argumentul
//semnaleaza o eroare
throw “arg este null”;
else
//semnaleaza o alta eroare
Throw “arg nu este null”
}
catch (e) { //capteaza eroarea de mai sus
//verifica erori
if (e = =”arg este null”)
//returneaza mesajul
return (e+”tratat local.”);
else
//eroarea nu se trateaza aici
throw e; //se trimite eroarea
}
}
catch (e) { // trateaza alte erori…
return (e + “tratat general.”);
}
}
Pentru
trycatch (“”); vom obtine “
arg este null tratat local.”, iar pentru
trycatch (“Web”) va rezulta
“arg nu este null tratat general.” .
Specificarea obictelor In JavaScript obiectele reprezinta perechi nume valoare, care in alte limbaje se regasesc sub denumirea de tablouri asociative (
hash)-asa cum se intampla in Perl PHP sau RUBY-ori ca
hashmaps in Java.
Numele este desemnat de un sir de caractere, iar valoarea pot fi de orice tip. Astfel, aceasta structura de date poate fi deosebita deversatila, specificata in mod dinamic.
Cu alte cuvinte, un obicet este o colectie de
proprietati, avand mai multe
atribute. Proprietatile pot contine alte obiecte, valori primitive sau metode. Sunt disponbile diverse obiecte predefinite, o parte dintre ele deja precizate:
global,object,function, array, string, Boolean, number, math, date. Un obiect poate fi creat in mod direct prin specificarea unor noi instante a unui obiect, cu operatorul
new, ca in exemplul urmator:
var ob=new object ();
var ob={}; // echivalent cu linia anterioara
Accesarea propietatilor se realizeaza precizand, in manierea dinamica, numele acestora
ob.nume = “Tux”;
var nume = ob.nume;
Putem considera un obiect JavaScript un tablou. Acest punct de vedere ne da posibilitatea sa accesam proprietatile unui obiect pe baza numelor, date drept indicii de tablou – forma general este urmatoarea:
numeObiect [“numeproprietate”].
Astfel o constructie echivalent celei dintr-un exemplu anterior este :
ob[“nume”] = “Tux”;
var nume = ob[“nume”];
Pentru a declara si a atasa valori proprietatilor unui obiect, vom putea recurge la o sintaxa precum:
var pringuin = {
nume : “Tux”,
proprietati : {
culoare : “verde”,
marime : 17
}
}
Aceasta ne permite sa specificam obiecte in cadrul altor obiecte. Accesul la culoarea unui pingiun se va face, de exemplu, prin:
pingiun[“proprietati”][“culoare”], iar marimea va fi data de
pingiun.proprietate.marime. Putem intera proprietatile – care sunt considerate cheie – cu ajutorul constructiei
for…in:
var pingiun = {“nume”: ”Tux”, “marime”: 17};
for (var proprietate in pingiun) {
print (proprietate + “=” + pingiun[proprietate]);
Functia
print() va trimite informatiile date ca argument spre imprimanta.
Tablouri Dupa cum aminteam mai sus, tablourile sunt tipuri speciale de obiecte: proprietatile (cheile) sunt desemnate prin valori numerice, si nu prin siruri de caractere.
Vom utilize sintexa deja cunoscuta de la obiecte:
var animale = new array (); //instantiem un tablou
animale[0] = “pingiun”;
animale[1] = “cal”;
animale[2] = “pterodactil”;
Dupa cum se poate observa, crearea unui tablou se realizeaza prin operatorul
new, forma generala fiind:
var nume = new array(element0, element1, …, elementN)
De asemenea, putem recurge la sintaxa alternative:
var animale = [“pingiun”, “cal”, “pterodactil”];
Spre deosebire de alte limbaje, in alocarea tablourilor se realizeaza in maniera contigua, in JavaScript se permite ca tablourile sa aiba “gauri”, dupa cum se poate remarca in exemplul de mai jos:
var animale = [“pingiun”, “cal”, “pterodactil”];
animale[33] = “om”;
In acest caz
animale.length va intoarce 34, dar
typeof animale[13] va avea ca rezultat
undefined Pentru a adauga elemente noi, vom folosi:
animale[animale.length] = altAnimal;
Daca la definirea unui tablou specificam toate valorile sale, atunci acel tablou se numeste
tablou dens.
Iterarea tuturor elementelor unui tablou se poate realize cu
for classic pe baza proprietatii
length:
for (var i=0; I <animale.length; i++) {
//de prelucrat animale[i]
}
//varianta mai buna
for (var i=0, j=animale.length; i<j; i++){
//de prelucrat animale[i]
}
Continutul unui tablou poate fi
eterogen.Exemplu
var animale = [10, “cal”, false, “crocodil”];
Sunt disponibile proprietatile
index, input, length si
prototype, dar si metodele:
to String(), concat(item, …), join(separator), pop(), push(kitem, …), reverse(), sort(functieComparare), shift(), splice(start, contor, [item..]), unshft([item…]). Obiectul Date()Date se foloseste pentru manipularea datei calendaristice si a timpului fiind predefinite o serie de metode utile pentru setarea, consultarea si modificarea datei.
Pot fi folositi mai mullti constructori predefiniti-similar limbajului Java:
var data = new Date();
var data = new Date(valoare);
var data = new Date (an, luna, zi[, ore [, minut [, secunde [, milisecunde]]]]);
Prima forma va initialize un obiect care va stoca data si ora curenta, conform masinii locale. A doua linie va initaliza un obiect memorand o data specifica in forma numerica (timpul UNIX in milisecunde: UTC-
Universal Coordinated Time, adica timpul scurs de la 1 ianuarie 1970) sau ca sir de caractere (“January 07, 1974”). A treia solutie posibila este sa furnizam fiecare componenta a datei (cele care lipsesc vor fi considerate nule). Numerotarea lunilor, orelor, minutelor, secundelor si milisecundelor porneste de la zero. Anul va fi specificat cu patru cifre.
Pot fi folosite diverse metode utile,, dintre care amintim:
getYear(), getMonth(), getDay(), getHours(), getMinutes(), getSeconds(), toString() si
toLocaleString(). Astfel in urma executiei codului:
var data = new Date (“January 07, 1974”);
data.toLocaleString();
Vom putea obtine sirul “7 ianuarie 1974 00:00:00”
FunctiiVom defini functii cu ajutorul cuvantului-cheie
function. De exemplu:
function aduna (x,y) //aduna doua valori
{
var total = x+y;
return total;
}
Daca nu e intors nimic in mod explicit – via
return -, valoarea de retur este
undefined. Parametrii pot lipsi, fiind considerati
undefined. Putem transmite mai multe argumente, dar cele in plus vr fi ignorate
In cazul nostru, aduna (12, 21, 10) va intoarce 33.
Rgumentele primate de o functie pot fi accesate prin intermediul tabloului
arguments care reprezinta o proprietate predefinita.
Astfel vom specifica o functie care aduna un numar arbitrar de valori:
function aduna () {
var suma = 0;
for (var i=0, j=arguments.length; i<j; i++) {
suma += arguments[i];
}
return suma;
}
Deoarece functiile sunt tot obiecte, ele pot fi foloite in maniera “anonima” – nu le precizam numele, ci doar corpul de instructiuni, eventual si argumentele de intrare. O exemplificare este urmatoarea:
var media = function() { // calculeaza media
var suma = 0;
for (var i=0, j=arguments.length: i<j; i++) {
suma +=arguments[i];
}
return suma/ arguments.length;
}
Desigur pot fi definite functii care se auroapeleaza recursive. In cadrul unei functii anonime pentru a cunoaste numele efectiv al functiei curente, se va utiliza
arguments.callee. Vom furniza un exemplu in sectiunea de prelucrarea documentelor XML in JavaScript.
Tot cu ajutorul aceseia, vom putea “salva” starea executiei codului. Liniile rmatoare sunt solutia prin care o functie isi “aminteste” de cate ori a fost apelata:
function contorizareApeluri () {
if (!arguments.callee.count) { //prima oara
arguments.callee.count = 0;
}
return arguments.callee.count++;
}
Clase definite de utilizatorVom specifica in continuare clase de obiecte, pe baza conceptului de
functie anonima si a obiectului
this.
Reluand exemplul cu animale, vom crea clasa Animal:
function Animal (nume, marima) {
this.nume = nume;
this.marime = marime; //date membre
this.oferaNume = function() { //metoda
return this.nume;
};
this.oferaMarime = function () { //metoda
return this.Marime;
};
}
Initierea unui obiect se va putea realize printr-o linie precum
var tux = new Animal (“Tux”, 17);
Operatorul
new creeaza un nou obiect vid si apeleaza functia specificata, cu
this setat pe acest obiect. Astfel, am specificat un
constructor care prin conventie trebuie sa aiba numele scris cu prima litera mare si trebuie apelat via
new.Desigur metodele clasei pot fi declarate si in exteriorul constructorului:
function oferaNumeAnimal () {
return this.nume;
}
function Animal (nume, marime) {
this.Nume = nume;
this.Marime = marime;
this.oferaNume = oferaNumeAnimal;
}
Prototipuri Ducand lucrurile mai departe, vom putea extinde structura unei clase pe baza conceptului de
prototip specificat de ECMAScript. Fiecare obiect poseda proprietatea
prototype prin care i se acceseaza – si chiar altereaza – structura interna.
Iata un exemplu:
//definitia initiala a clasei
function Animal (nume, marime) {
this.nume = nume;
this.marime = marime;
}
…
//inseram dynamic alte metode, mai tarziu
animal.prototype.oferaNume = function () {
return this.nume;
}
animal.prototype.oferaNume = function () {
return this.marime;
}
Ulterior, putem extinde clasa, definind o noua metoda:
animal.prototype.oferaNume = function () {
return this.nume.toUpperCase ();
}
Mai mult decat atat, putem extinde sau schimba comportamentul obiectelor JavaScript predefinite. In exemplul de mai jos, specificam o noua metoda asociata tipulul
String:string.prototype.inverseaza = function () {
var inv = ‘’;
//inverseaza sirul
for (var i=this.length-1; i>=0;i--) {
Inv += this[i];
}
return inv;
}
Un apel ulterior de forma ”Web”.inverseaza() va conduce la rezultatul “beW”.
Pentru a cunoaste tipul unui obiecte – pe baza constructorului si a ierarhiei de prototipuri – se foloseste operatorul
instanceof. Astfel, daca declaram
var marimi = (17, 20, 7, 14); atunci rezultatul pentru
mrimi instanceof String va fi – asa cum era de asteptat –
false. In situatia clase
Animal si a obiectului
tux, linia
tux instanceof Animal furnizeaza valoarea
true iar
tux instanceof Function intoarce
false.
Cel mai general
prototype este cel al lui
Object.Pentru suprascrie metoda
toString(), care ulterior va avea un nou comportament pentru toate instantele (obiectele) prezente intr-un program.
Astfel, daca initial pentru lini
tux.toString() obtinem
“[object Object]” (reprezinta un obiect), dupa ce specificam urmatoarele linii:
//suprascriere
animal.prototype.toString = function () {
return ‘<animal>’ + this.oferaNume () + ‘</animal>’;
}
vom primi
<animal>Tux</animal> Aceasta facilitate ofera support pentru serializarea, dar implica riscuri in cadrul programelor.
Functii de nivel inalt Este foarte important de retinut ca, deoarece o functie reprezinta un obiect, poate fi stocata intr-o variabila, transmisa unei alte functii sau intoarsa de o functie (fiind argument pentru
return).
Acest aspect ne ofera posibilitatea sa concepem programe care isi schimba comportamentul in mod dimanic, gradul de versatilitate fiind unul mai ridicat decat in cazul limbajelor compilate.
Drept exemplificare, presupunem ca intentionam sa calculam greutatea unui animal, dupa formula
greutate – marime*33 Pentru varianta clasica, putem scrie liniile:
var marimi = [17, 20, 7, 14];
var greutati = [];
for (var i=0;i<marimi.length; i++) {
greutati[i] = marimi[i] * 33;
}
Daca formula de calcul a greutatii se schimba, va trebui sa modificam codul, ceea c ear putea conduce la erori in cazul programelor mai complicate.
O versiune imbunatatita, independenta de modul de calcul al greuttii este urmatoarea – vom utilize o functie generica:
// genereaza un tablou de greutati,
//calculate conform functiei ‘calcul’
functiongenGreutati (tablou, calcul) {
var rez = []; //tablou vid
for (var i=0; i<tablou.length; i++) {
rez[i] = calcul (tablou[i]);
}
return rez;
}
//functia de calcul
function calculGreutate (marime) {
return marime * 33;
}
//rezultatul
greutati = gen Greutati (marimi, calculGreutate);
Suportul pentru incapsulare Exista posibilitatea sa apara conflicte privind denumirea functiilor/variabilelor specificate de programe diferite, ale mai multor utilizatori deoarece JavaScript ofera un singur spatiu de nume, la nivel global.
O practica indicate ar fi sa pastram codul –sursa la nivel privat, pentru a nu afecta spatial de nume global. Astfel, codul poate fi complet incapsulat via functii anonime care “pastreaza” constructiile la nivel privat, fara a interfera cu alte programe (cu diverse bilioteci JavaScript pe care le folosim).
In exemplul de mai jos, incapsulam intreg programul nostrum astfel:
var cod = (function () {
var n = 3; //variabila private
function start (x) {
//…poate accesa ‘n’ si functia “faCeva”
}
function faAia (param) {
//…invizibila din afara
}
function faCeva (x,y) {
//…poste accesa ‘n’ si functia ‘faCeva’
}
return {
//sunt publicate doar functiile ‘start’ si ‘faCeva’
‘start’ : start,
‘faCeva’ : faCeva
}
}) ();
cod.start (x); //apelam ‘start’
In acest mod, putem avea si membrii privti ai claselor pe care le specificam.