Aspecte elementare ale limbajului JavaScriptThis is a featured page

1.Mediul de executie

Asa 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 date

Standardul 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”


Functii

Vom 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 utilizator

Vom 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.


anomistu
anomistu
Latest page update: made by anomistu , Jul 1 2008, 6:12 PM EDT (about this update About This Update anomistu Edited by anomistu

3251 words added

view changes

- complete history)
Keyword tags: None
More Info: links to this page
There are no threads for this page.  Be the first to start a new thread.