Execució de programes al costat client. Introducció a JavaScript
En l’inici de l’era d’Internet, les pàgines web eren bàsicament estàtiques, mostraven un contingut fix sense possibilitat d’interacció amb l’usuari. Però des de l’aparició de la Web 2.0, si una cosa defineix el món web és l’increment d’interacció i usabilitat, millorant l’experiència de l’usuari en la navegació. És aquí on el llenguatge JavaScript té un rol important. La programació en el cantó del client codificada dins les pàgines web és la responsable de fer pàgines web atractives tal com les coneixem avui dia.
Els autors de JavaScript van proposar el 1997 a la European Computer Manufacturers Association (ECMA) que el seu llenguatge s’adoptés com a estàndard. Així és com va néixer el ECMAScript, que avui dia és estàndard ISO. En els inicis d’Internet hi havia bastants problemes de compatibilitat entre navegadors (Internet Explorer, Netscape Navigator, Opera) a diferents nivells, i això va fer prendre consciència de la importància en l’adopció d’estàndards. El World Wide Web Consortium (WWWC) és una organització que es cuida de vetllar per la implementació d’estàndards en l’àmbit web, fent possible que diferents fabricants de programari (software) es posin d’acord en benefici de l’usuari final.
Per tal d’avançar en la compatibilitat entre navegadors web, no només és important que implementin el motor de JavaScript segons els estàndards ECMAScript, sinó que els diferents elements que hi ha en una web (botons, caixes de text, enllaços…) es comportin de la mateixa manera i responguin als mateixos events (esdeveniments). És per això que el WWWC va definir el Document Object Model (DOM, o Model d’Objectes del Document), que defineix un estàndard d’objectes per representar documents HTML i/o XML.
Introducció al llenguatge JavaScript
JavaScript és un llenguatge de guions (script, en anglès), implementat originàriament per Netscape Communications Corporation, i que va derivar en l’estàndard ECMAScript. És conegut sobretot pel seu ús en pàgines web, però també es pot utilitzar per realitzar tasques de programació i administració que no tenen res a veure amb la web.
Malgrat el seu nom, JavaScript no deriva del llenguatge de programació Java, tot i que tots dos comparteixen una sintaxi similar. Semànticament, JavaScript és més pròxim al llenguatge Self (basat també en l’ECMAScript).
El nom JavaScript és una marca registrada per Oracle Corporation.
Avui dia JavaScript és un llenguatge de programació madur, de manera que sobre d’ell s’han implementat diferents tecnologies, capes d’abstracció, biblioteques i frameworks (entorns de treball) que amaguen els rudiments bàsics del llenguatge. Com a tecnologia podem parlar d’AJAX, un estàndard per intercanviar informació entre el client i el servidor web, que agilitza la comunicació i l’ample de banda, i millora l’experiència d’usuari. Quant a biblioteques hem de parlar de JQuery, que porta la programació amb JavaScript a un esglaó superior, i d’aquesta manera el programador web pot obtenir resultats espectaculars amb poques línies de codi. Quant a frameworks, el més utilitzat avui dia és Angular JS.
El mes de juny del 2015 es va publicar l’edició ES2015 del llenguatge (inicialment conegut com a ES6). A partir d’aquesta edició, es van publicant noves actualitzacions del llenguatge cada any, actualitzades amb el nom de l’any corresponent: ES2015, ES2016, ES2017, etc. Tot i que algunes de les característiques publicades en l’especificació no són implementades per tots els navegadors, és d’esperar que totes siguin funcionals pròximament.
Cal tenir en compte que aquests materials pedagògics s’han redactat d’acord amb l’edició ES2020, encara que la major part dels exemples són compatibles amb les edicions ES2015 i posteriors.
Aquesta edició conté canvis molt importants i afegeix nova sintaxi per desenvolupar aplicacions complexes, incloent-hi la declaració de classes i mòduls.
Tot i que els navegadors moderns admeten sense problemes ES2015, encara hi ha molt programari desenvolupat que utilitza la sintaxi d’ECMA-262 5a edició, ja que les noves edicions són compatibles amb les anteriors. És a dir, és possible actualitzar una aplicació implementada en una versió anterior afegint nou codi que faci servir les funcionalitats afegides en una versió posterior com ara ES2020.
ECMA-262 5a edició
També es coneix com a JavaScript 5. És l’edició anterior a ES2015, i la seva especificació va ser publicada el desembre del 2009.
Podeu trobar un resum de programació amb ECMA-262 5a edició les “Referències” d’aquesta unitat.
Llenguatges compilats i interpretats. Llenguatges de guions
Els llenguatges de programació es divideixen, quant a la manera d’executar-se, entre els interpretats i els compilats. Alguns llenguatges interpretats s’anomenen també llenguatges de guions (scripts en anglès).
Els llenguatges interpretats s’executen mitjançant un intèrpret, que processa les ordres que inclou el programa una a una. Això fa que els llenguatges interpretats siguin menys eficients en temps que els compilats (típicament 10 vegades més lents), ja que la interpretació de l’ordre s’ha de fer en temps d’execució. Com a avantatges podem dir que són més fàcils de depurar i són més flexibles per aconseguir una independència entre plataformes (portabilitat).
D’altra banda, els llenguatges compilats necessiten del compilador com a pas previ a l’execució. Com a resultat de compilar el codi font (les instruccions que ha codificat el programador) s’obté el codi màquina (codi binari que és proper al microprocessador), i això fa que la seva execució sigui eficient en temps.
Exemples de llenguatges interpretats: JavaScript, Python, Perl, PHP, Bash…
Per veure la diferència, mostrarem un exemple amb JavaScript (llenguatge interpretat) i un exemple amb llenguatge C (llenguatge compilat).
Exemples de llenguatges compilats: C, C++, Pascal, Fortran…
Amb JavaScript, calculem el factorial de tres números, els sumem, i mostrem la suma en la pantalla del navegador, dins d’una etiqueta HTML que ens serveix de contenidor:
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>Funció factorial recursiva</title> </head> <body> <h1>Funció factorial recursiva</h1> <p id="p1"></p> <script> let num1 = factorialRecursiu (5); let num2 = factorialRecursiu (6); let num3 = factorialRecursiu (7); document.getElementById("p1").innerHTML = num1+num2+num3; function factorialRecursiu (n) { if (n == 0){ return 1; } return n * factorialRecursiu (n-1); } </script> </body> </html>
Podeu provar el darrer exemple a: codepen.io/ioc-daw-m06/pen/dyGwGBd
Amb llenguatge C podem fer una petita aplicació equivalent, compilant el codi factorialRecursiu.c:
#include<stdio.h> int factorialRecursiu (int n) { int r; if (n==1) return 1; r=n*factorialRecursiu(n-1 ) ; return (r ) ; } int main() { int res = 0; printf("Factorial Recursiu\n" ) ;; res += factorialRecursiu(5); res += factorialRecursiu(6); res += factorialRecursiu(7); printf("La suma és %d\n",res) ; return 0; }
Per compilar-lo, executem en la línia d’ordres:
gcc -o factorialRecursiu factorialRecursiu.c
Es generarà l’executable factorialRecursiu. Aquest és un fitxer binari, per tant no el podem obrir com un document de text. El seu contingut és el codi objecte que sap executar el processador. És un codi optimitzat i ràpid. Segur que el càlcul per trobar els tres factorials i sumar-los es fa de manera molt més ràpida que en JavaScript. Per executar-lo:
$ ./factorialRecursiu Factorial Recursiu La suma és 5880
El procés de compilació amb gcc aquí descrit és el cas més simple possible. En aplicacions més completes, el procés de compilació implica generar els fitxers “objecte” i enllaçar-los (link) amb les llibreries per tal de generar el fitxer executable.
Java, un cas especial
No tots els llenguatges de programació entren en la divisió entre compilats i interpretats. En el cas del llenguatge Java, el codi font és independent de la plataforma en què es programa. El compilador de Java genera un codi màquina especial (el bytecode), i la Màquina Virtual de Java (JVM), existent per a cada plataforma (Linux, Windows, Solaris), sap interpretar aquest bytecode, executant-lo. D’aquesta manera, escrivint una sola vegada el codi, s’obtenen aplicacions multiplataforma.
Hi ha un tipus especial de compilació anomenada transpiling, que consisteix en la conversió d’un codi en un llenguatge en un altre. Hi ha molts llenguatges que fan servir aquest tipus de compilació per compilar a JavaScript, és més, aquest és el sistema idoni per assegurar la compatibilitat del programari entre versions. Per exemple, abans que les noves funcionalitats d’ES2015 fossin implementades àmpliament als navegadors, era possible escriure la implementació d’una aplicació amb ES2015 i “transpilar-la” a JavaScript 5, assegurant-ne així la compatibilitat.
Entre els principals llenguatges que es compilen a JavaScript mitjançant transpiling hi ha els següents:
Compatibilitat amb navegadors
Actualment tots els navegadors s’actualitzen automàticament. Per això es pot confiar en el fet que és segur utilitzar les noves funcionalitats del llenguatge. En cas de dubte es pot consultar la compatibilitat de les noves funcionalitats en el següent enllaç: bit.ly/2OFHQDp
- JavaScript: és molt freqüent fer conversions d’una versió més actual a una altra de més antiga per assegurar la compatibilitat.
- Dart: desenvolupat per Google, s’utilitza principalment en el desenvolupament d’aplicacions mòbils (iOS i Android natives) mitjançant el marc de treball Flutter.
- TypeScript: és un superconjunt de JavaScript desenvolupat per Microsoft. Per consegüent, qualsevol programa desenvolupat amb JavaScript és vàlid a TypeScript. Aquest llenguatge afegeix noves característiques al llenguatge i cal compilar-lo a JavaScript per poder executar les aplicacions al navegador. És el llenguatge recomanat per treballar amb el marc de treball Angular.
Els “transpiladors” (com Babel), a més de fer la conversió del codi, acostumen a afegir els polyfills. Un polyfill és una implementació d’una funcionalitat del llenguatge que pot ser que encara no es trobi implementada als navegadors. Primer es comprova si la funcionalitat existeix i, en cas contrari, s’afegeix la implementació alternativa.
El motor de JavaScript. Execució de JavaScript en línia d'ordres
El llenguatge de JavaScript existeix independentment de la web. És necessari recalcar-ho, ja que la manera habitual de treballar és integrant el llenguatge JavaScript dins les pàgines web (HTML).
Quan executem JavaScript dins una pàgina web, per exemple amb el navegador Mozilla Firefox, de l’execució del codi JavaScript se n’encarrega el motor de JavaScript. SpiderMonkey és el motor de JavaScript de Mozilla, que es pot instal·lar de forma independent sense estar associat a un navegador web. Trobareu més informació sobre Spidermonkey i com instal·lar-lo en els següents enllaços:
Si s’instal·la, de forma opcional, l’SpiderMonkey (tant per a sistemes Linux com Windows), es pot practicar el llenguatge JavaScript des de la línia d’ordres, en un entorn que res a veure té amb la web. Per tant, JavaScript és un llenguatge que existeix independentment de la web.
Així com SpiderMonkey és un motor de JavaScript implementat amb C/C++, Rhino també és un motor de JavaScript, en aquest cas implementat amb Java. També és un projecte desenvolupat per la fundació Mozilla:
Un altre ús molt habitual de JavaScript des de la línia d’ordres és la creació d’eines o aplicacions de servidor mitjançant Node.js. Es tracta d’un entorn d’execució per executar programes escrits en JavaScript, cosa que permet crear aplicacions de xat, servidors web i eines per automatitzar tasques. Es pot descarregar i consultar la documentació a:
Aplicatius web: arquitectura client-servidor
Avui en dia tenim una gran quantitat de dispositius connectats al núvol. El portàtil, el telèfon mòbil, la tauleta, la televisió… i amb l’Internet of Things (IoT) cada vegada aquests dispositius aniran en augment. Una de les coses habituals que fem amb algun d’aquests dispositius és navegar per la web tot visitant pàgines web.
D’altres dispositius i aplicacions, tot i que no serveixen per navegar per la web, sí que fan consultes a serveis web. Imaginem per exemple un aplicatiu web per controlar una casa domòtica, on podem posar la calefacció a distància. O una aplicació per al mòbil de calendari, que se sincronitza contínuament i ens avisa de quan tenim una reunió. Per tant, hem de prendre consciència del fet que el que ens permet la tecnologia web, avui dia, és molt més que visitar pàgines web.
Si una cosa tenen en comú tots aquests aplicatius web és que es basen en l’arquitectura client-servidor. Quan parlem de client web ens ve el cap el navegador web (Firefox, Chrome, Internet Explorer). Ara bé, una aplicació que ens permet consultar l’horari de la parada del bus també és un client web, tot i que la presentació no té format de pàgina web.
A l’altra banda tenim els servidors web, que serveixen el contingut web que tenen allotjat. Exemples de servidor web són l’Apache Web Server o l’Internet Information Server, però també el servidor d’aplicacions Tomcat (vegeu la figura).
Quan ens connectem a una pàgina web per Internet, el més habitual dels casos és que ens connectem a un servidor web Apache.
Suposem que estem amb el nostre portàtil, i consultem una web de viatges des del navegador Chrome. Fixem-nos amb el camí que fan els paquets IP. Des del Chrome, en la barra de navegació, posem una URL que identifica unívocament el contingut que volem baixar (per exemple, www.vacances.com/pallars_jussa.php). Aquesta informació es trosseja en forma de paquets IP que viatgen per Internet. Els servidors DNS localitzen la direcció IP del servidor on està allotjat el contingut. A més, aquests paquets han de passar per routers, firewalls i diferents capes de seguretat.
Els paquets IP finalment s’ajunten en el destí, on tenim un servidor web Apache que allotja l’script pallars_jussa.php. Aquest script s’executa (es processa la part de PHP i s’ajunta amb la part d’HTML), i finalment el resultat es serveix al destinatari fent el camí invers.
Quan la pàgina web arriba al destí, en el navegador web podem visualitzar la informació demanada. El navegador web, el nostre client, és el responsable de maquetar correctament la pàgina web a partir del contingut HTML, els fulls d’estil CSS, i també el codi JavaScript que conté. Podem veure el codi font que hem rebut del servidor fent Ctrl/U en el navegador (fixa’t que mai veuràs en el codi font el codi PHP; per contra, sí que pots veure el codi JavaScript).
L’entorn de client que estem estudiant és precisament la part del navegador web (Firefox, Chrome), en contraposició amb la part de servidor (el servidor Apache). Així com el codi PHP s’executa en el servidor, el codi JavaScript que contenen les webs s’executa en el client (Firefox, Chrome).
Exemple de navegador web en mode text
Què passa si a una pàgina web li traiem tota la part gràfica i de disseny? No us hauria d’estranyar aquesta possibilitat si us poseu en la pell de les persones cegues. Els desenvolupadors web han de pensar en el concepte d’accessibilitat: intentar que el contingut i la funcionalitat bàsiques d’una web estigui disponible en un navegador web en mode text.
Instal·lar un navegador web que funcioni en la consola, sense interfície gràfica, és simple. Hi ha diferents possibilitats, entre elles, Lynx. Us podeu instal·lar Lynx tant en Linux com en Windows. Per a sistemes Linux basats en paquets Debian/Ubuntu és tan fàcil com fer:
$ sudo apt-get install lynx.
Us serà molt útil consultar aquests enllaços:
Un cop el teniu instal·lat, des de la consola podeu visitar una pàgina web:
lynx www.google.com
I també podeu seguir els enllaços presents. Ara bé, té més sentit si visiteu una pàgina que tingui un fort component textual:
lynx https://ca.wikipedia.org/wiki/Tirant_lo_Blanc
Els navegadors textuals no executen codi JavaScript, a causa de motius tècnics, i al fet que la interacció amb l’usuari és molt limitada.
Beneficis d'utilitzar JavaScript en les pàgines web
En l’inici de l’era d’Internet les pàgines HTML eren estàtiques: només hi havia text fix i imatges. Avui dia, tot al contrari, les pàgines web són dinàmiques, amb efectes visuals i interacció amb l’usuari. Això ha estat possibles gràcies a la incorporació de JavaScript: el codi que s’executa dins el navegador web.
Vegeu-ne algun dels avantatges:
- JavaScript és l’únic llenguatge admès pels navegadors; per consegüent, no és possible fer servir altres llenguatges sense utilitzar un “transpilador” a JavaScript.
- JavaScript permet una gran quantitat d’efectes dins les pàgines. Entre d’altres: popups, text que es col·lapsa, capes que es fonen, scrolls, transicions d’imatges…
- JavaScript afegeix interactivitat amb l’usuari.
- JavaScript permet validació dels formularis en el cantó del client. Una validació inicial dels formularis és possible per eliminar simples errors, com assegurar-se de què el format de data, DNI, correu electrònic o telèfon són correctes… Com a resultat, l’usuari té una resposta més ràpida que no pas si el control dels errors el fes el servidor.
- JavaScript permet accedir a informació del navegador, cosa que inclou el sistema operatiu i el llenguatge.
- JavaScript permet el desenvolupament d’extensions per a navegadors com Chrome i Firefox.
- JavaScript permet el desenvolupament d’aplicacions complexes com els editors de text o fulls de càlcul de Google Docs i videojocs (per exemple: bit.ly/3hoeO7T).
Desavantatges de JavaScript
La seguretat és el principal problema a JavaScript. Els fragments de codi JavaScript que s’afegeixen a les pàgines web es descarreguen en els navegadors i s’executen en el cantó del client, permetent així la possibilitat que un codi maliciós es pugui executar en la màquina client i així explotar alguna vulnerabilitat de seguretat coneguda en les aplicacions, navegadors o fins i tot del sistema operatiu. Existeixen estàndards de seguretat que restringeixen l’execució de codi per part dels navegadors. Es tracta sobretot de deshabilitar l’accés a l’escriptura o lectura de fitxers (exceptuant les galetes). Tanmateix, la seguretat a JavaScript continua sent un tema polèmic i de discussió.
Podeu trobar un exemple de videojoc a la unitat “Desenvolupament de casos pràctics”.
Un altre desavantatge de JavaScript és que tendeix a introduir una quantitat enorme de fragments de codi en els nostres llocs web. Això es resol fàcilment emmagatzemant el codi JavaScript en fitxers externs amb extensió JS. Així la pàgina web queda molt més neta i llegible. La tendència actual és de fet separar totalment la part del contingut HTML, la part de funcionalitat JavaScript, i la part de disseny i maquetació. De manera que tres perfils d’usuaris diferents (el que genera continguts HTML, el programador web, i el dissenyador) puguin estar treballant en el mateix projecte, i tocant arxius diferents.
Un avantatge de deixar ben net el contingut HTML és que estem facilitant la feina als motors de cerca (com Google), de manera que poden desxifrar fàcilment el contingut de la pàgina web, i aquest contingut es pot indexar correctament en els resultats de les cerques.
Integració del codi JavaScript en el codi HTML. Primer exemple
JavaScript és un llenguatge d’script que existeix i té sentit fora de la web, però que ha pres protagonisme i reconeixement gràcies a la web. Així doncs, és usual associar JavaScript amb una de les tecnologies clau en el desenvolupament d’aplicacions web. Per a nosaltres JavaScript serà un puntal per tal que les nostres aplicacions web es comportin de forma dinàmica i interactiva. El primer que caldrà veure és com conviuen la sintaxi HTML i la sintaxi JavaScript, com es pot integrar JavaScript dins les pàgines web.
La inclusió de la sintaxi de JavaScript es fa mitjançant l’etiqueta <script>.
Etiqueta <script>
És usual veure escrita la sintaxi <script type=“text/javascript”>. Aquest atribut ja no és necessari ja que en HTML5 el llenguatge JavaScript és el llenguatge d’script per defecte. Tanmateix, això reforça la idea que podrien haver-hi d’altres llenguatges d’script que el navegador sabés interpretar.
<script> let nom = "Rita"; alert("Hola " + nom); </script>
Podem escriure moltes etiquetes <script> en un document, tot i que posar-ne una de sola és un bon hàbit. Aquestes etiquetes les podem posar tant en el <head> com en el <body>.
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>El meu primer exemple</title> <script> function funcio() { document.getElementById("paragraf").innerHTML = "Escrivim en el paràgraf"; } </script> </head> <body> <h1>El meu primer exemple</h1> <p id="paragraf">Text original del paràgraf</p> <button type="button" onclick="funcio()">Clica'm</button> </body> </html>
Per provar aquest codi, obriu el vostre editor de text preferit. Però ens referim a un editor de text pla, no utilitzeu Openoffice, Microsoft Office o similars. A Linux podeu utilitzar el Gedit, o fins i tot editors de consola: Vim, Nano, Joe. A Windows podeu utilitzar el Notepad. Tot i que els desenvolupadors web utilitzen editors més complets, en aquesta ocasió utilitzareu un editor senzill. Un cop obert l’editor, copieu el codi i reanomeneu-lo a un fitxer HTML, per exemple, boto.html. Tingueu clar en quina ubicació l’heu gravat. Aquest fitxer l’heu d’obrir des del vostre navegador web preferit (preferentment Mozilla Firefox o Google Chrome). Ho podeu fer clicant sobre el fitxer amb el botó dret, o a la barra de navegació del navegador ficar la ruta. Per exemple:
file://ruta_al_fitxer/boto.html
Us ha d’aparèixer una web senzilla. Quan cliqueu sobre el botó, el text del paràgraf ha de canviar. Ja heu fet la vostra primera aplicació JavaScript. Ja heu afegit interacció a una pàgina web. En aquest codi es donen per suposats molts conceptes. Primer de tot s’ha afegit un event a un botó. Un event és la capacitat de respondre a una acció de l’usuari, en aquest cas fer clic sobre un botó. Quan es fa clic, s’executa la funció funcio()
definida entre les etiquetes <script>. Aquesta funció el que fa és localitzar l’element definit per l’identificador id=“paragraf”, que és un paràgraf (<p>), i canvia el seu contingut. Aquest codi també funciona si col·loqueu l’etiqueta <script> en una altra banda:
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>El meu primer exemple</title> </head> <body> <h1>El meu primer exemple</h1> <p id="paragraf">Text original del paràgraf</p> <button type="button" onclick="funcio()">Clica'm</button> <script> function funcio() { document.getElementById("paragraf").innerHTML = "Escrivim en el paràgraf"; } </script> </body> </html>
Introducció a la manipulació del DOM
El DOM (Document Object Model) és una interfície per a documents HTML i XML que representa un document com nodes i objectes, de manera que els llenguatges de programació poden connectar amb la pàgina i modificar-la.
És interessant col·locar l’etiqueta <script> a sota de tot del <body>. D’aquesta manera no bloquem la càrrega dels elements HTML. Tanmateix, s’ha de donar preferència a col·locar les etiquetes <script> a la capçalera <head> del document web.
Podeu trobar informació molt més completa a la unitat “Model d’objectes del document”.
En el context dels navegadors web, les pàgines web són documents i el navegador és el responsable d’interpretar el codi HTML per generar el DOM, que pot ser modificat mitjançant JavaScript.
El DOM és accessible des de JavaScript mitjançant l’objecte global document
, que exposa múltiples mètodes per consultar-lo i modificar-lo.
Per seleccionar un node del document, el més simple és utilitzar el mètode getElementById()
, que permet seleccionar un element pel seu identificador (l’atribut id
). Per exemple:
Quan s’assigna un identificador a un element HTML (atribut id
) cal que aquest sigui únic.
<html> <body> <h1 id="titol">Aquést és el títol</h1> <p id="text">Això és un text</p> <script> let nodeTitol = document.getElementById('titol'); let nodeText = document.getElementById('text'); </script> </body> </html>
Vegeu que s’han assignat a les variables nodeTitol
i nodeText
els nodes de tipus element corresponents als identificadors titol
i text
, respectivament.
Cal destacar que són nodes de tipus element
. Així doncs, a partir d’aquestes variables es pot accedir a les propietats i els mètodes exposats per la interfície Element
. Per exemple, la propietat innerHTML
permet modificar el contingut intern d’un element:
Element
Podeu trobar informació detallada de la interfície Element
a mzl.la/3a9vQ8N.
nodeTitol.innerHTML = "Títol reemplaçat"; nodeText.innerHTML = "Nou contingut de <b>text</b>";
Podeu veure aquest exemple a l’enllaç següent: codepen.io/ioc-daw-m06/pen/vYyyQQp?editors=1000.
Fixeu-vos que es reemplaça el contingut intern de l’element, així que qualsevol contingut anterior es perd i, al mateix temps, si s’ha afegit codi HTML, aquest és interpretat i afegit com a nous elements al DOM (per exemple, <b>text</b>
s’afegeix un nou element dintre del paràgraf).
Atès que innterHTML
és una propietat, és possible modificar-la, consultar el seu valor i assignar-la a altres variables.
Per afegir un element nou al document primer cal crear-lo mitjançant el mètode createElement()
i indicar el tipus d’element. Per exemple:
document.createElement('li');
Però això no és suficient, ja que aquest element no s’ha afegit al document. Per afegir-lo hem de fer servir el mètode append()
que es troba definit a la interfície ParentNode
i que és implementada per Document
i Element
; això vol dir que es poden afegir nous elements tant al document com en d’altres elements.
Així, doncs, és necessari assignar l’element creat amb createElement
a una variable i obtenir una referència a l’element pare on es vol afegir el node. Per exemple, podem afegir nous elements a una llista de la següent manera:
<html> <body> <ul id="llista"></ul> <script> let llista = document.getElementById('llista'); let nouElementLlista = document.createElement('li'); llista.append(nouElementLlista); nouElementLlista.innerHTML = 'Primer element'; </script> </body> </html>
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/WNooPKX?editors=1000.
De la mateixa manera que podem afegir elements, també els podem modificar. Per fer-ho, només ens cal una referència al node i invocar al seu mètode remove()
:
<html> <body> <h1 id="primer">Primer títol</h1> <h1>Segon títol</h1> <script> let títol = document.getElementById('primer'); títol.remove(); </script> </body> </html>
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/OJbbqNz?editors=1000.
Per accedir al valor dels elements de formulari, com són els quadres de text i les llistes desplegables, s’ha de fer servir la propietat value
:
<html> <body> <h1 id="titol">Aquest és el títol</h1> <h2 id="subtitol">Aquest és el subtítol</h2> <label>Nou text:</label> <input id="entradaTitol" /> <label>Selecciona:</label> <select id="selectTitol"> <option value="Aquest és el primer subtítol">Primer</option> <option value="Aquest és el segon subtítol">Segon</option> <option value="Aquest és l'últim subtítol">Últim</option> </select> <button onclick="canviar();">Canviar Títols</button> <script> let nodeTitol = document.getElementById('titol'); let nodeSubtitol = document.getElementById('subtitol'); let nodeEntrada = document.getElementById('entradaTitol'); let nodeSelect = document.getElementById('selectTitol'); nodeEntrada.value = "nou títol"; function canviar() { nodeTitol.innerHTML = nodeEntrada.value; nodeSubtitol.innerHTML = nodeSelect.value; } </script> </body> </html>
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/MWbbLYE?editors=1000.
Fixeu-vos que hem assignat el valor de nodeEntrada
a “nou títol”
fàcilment, però en el cas de la llista seleccionable s’ha de tenir en compte que es treballa amb dos valors diferents, corresponents als parells assignats mitjançant l’element d’HTML option
:
- value: valor que s’assignarà a la llista desplegable.
- text: text que es mostra a la llista desplegable.
Per poder accedir a les opcions de l’element select
es pot utilitzar la propietat options
i selectedIndex
:
- options: és un array d’elements d’opció a partir dels quals es pot obtenir el seu
value
iinnerHTML
. - selectedIndex: és l’índex corresponent a l’opció seleccionada.
Donat que el contingut de la llista és determinat pels elements de tipus option
, si volem afegir més opcions a la llista és necessari crear nous elements i afegir-los, com es pot apreciar en el següent exemple:
<html> <body> <select id="seleccio"> <option value="Barcelona">Barcelona</option> </select> <script> let seleccio = document.getElementById('seleccio'); let novaOpcio1 = document.createElement('option'); seleccio.append(novaOpcio1); novaOpcio1.innerHTML = "Girona"; let novaOpcio2 = document.createElement('option'); seleccio.append(novaOpcio2); novaOpcio2.innerHTML = "Lleida"; let novaOpcio3 = document.createElement('option'); seleccio.append(novaOpcio3); novaOpcio3.innerHTML = "Tarragona"; </script> </body> </html>
Com que les opcions són elements, si volem eliminar un element de la llista cal eliminar el node corresponent cridant al seu mètode remove()
. Per exemple:
novaOpcio1.remove();
Atès que a partir d’un element de tipus select
podem accedir a la llista d’opcions contingudes, s’hi pot accedir sense necessitat d’un identificador únic. Per exemple:
let seleccio = document.getElementById('seleccio'); seleccio.options[0].value; // Mostra el valor de la primera opció de la llista seleccio.options[0].innerHTML; // Mostra el text de la primera opció de la llista seleccio.options[seleccio.selectedIndex].remove(); // Elimina l´opció seleccionada de la llista
Com es pot apreciar, es pot accedir a qualsevol opció mitjançant l’índex (posició a la llista començant per 0), ja que es tracta d’un array.
Cal tenir en compte que el tractament de les caselles de selecció i els botons d’opció és una mica diferent dels anteriors, ja que l’estat (seleccionat o no) no està assignat al valor, sinó que es tracta de la propietat checked
. Això és així perquè quan es treballa amb formularis s’envien els elements marcats, associant al nom del camp amb el valor assignat al botó.
<html> <body> <input type="radio" name="porta" id="boto-obert" value="oberta" checked/> <input type="radio" name="porta" id="boto-tancat" value="tancada" /> <script> let botoTancat = document.getElementById('boto-tancat'); botoTancat.checked = true; </script> </body> </html> </html>
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/oNYYVeY?editors=1000.
Fixeu-vos que al codi HTML s’ha indicat que l’opció marcada és oberta (l’atribut checked
es troba present), però amb botoTancat.checked = true
canviem l’opció a botoTancat
i, com que pertanyen al mateix grup, es desmarca l’anterior.
Els botons d’opció s’agrupen pel valor de l’atribut name
.
Les caselles de selecció s’utilitzen de la mateixa manera, amb la diferència que no estan agrupades i, per consegüent, poden marcar-se múltiples caselles alhora:
<html> <body> <input type="checkbox" id="opcio1" value="groc" checked/><label>Groc</label> <input type="checkbox" id="opcio2" value="vermell" /><label>Vermell</label> <input type="checkbox" id="opcio3" value="blau" /><label>Blau</label> <script> let opcio3 = document.getElementById('opcio3'); opcio3.checked = true; </script> </body> </html>
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/GRNmjXB?editors=1000.
Heu de tenir en compte que quan s’envia un formulari, en el cas de les caselles de selecció i els botons de ràdio només s’envien al servidor si estan marcats. Però si treballeu amb el client mitjançant JavaScript, haureu de controlar quines caselles estan marcades i quines no. Per exemple:
<html> <body> <h1>Opcions adicionals</h1> <input type="checkbox" id="opcio1" value="325" /><label>64MB Memoria Ram</label> <input type="checkbox" id="opcio2" value="265" /><label>2TB SSD</label> <input type="checkbox" id="opcio3" value="1445" /><label>GeForce GTX 3090</label> <button onclick="calcular();">Calcular</button> <h2>Cost extras: <span id="extra">0</span>€</h2> <script> let opcio1 = document.getElementById('opcio1'); let opcio2 = document.getElementById('opcio2'); let opcio3 = document.getElementById('opcio3'); let extra = document.getElementById('extra'); function calcular() { let total = 0; if (opcio1.checked) { total += Number(opcio1.value); } if (opcio2.checked) { total += Number(opcio2.value); } if (opcio3.checked) { total += Number(opcio3.value); } extra.innerHTML = total; } </script> </body> </html>
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/gOLLEKz?editors=1000.
Fitxers JavaScript externs
Un fitxer JavaScript, amb extensió JS, és un fitxer de text que conté instruccions JavaScript. Des de l’HTML podem incloure fàcilment un fitxer JavaScript:
<script src="fitxerExtern.js"></script>
Així doncs, el contingut de fitxerExtern.js serà:
function funcio() { document.getElementById("paragraf").innerHTML = "Escrivim en el paràgraf"; }
i el nostre codi quedarà de la següent manera:
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>El meu primer exemple</title> <script src="fitxerExtern.js"></script> </head> <body> <h1>El meu primer exemple</h1> <p id="paragraf">Text original del paràgraf</p> <button type="button" onclick="funcio()">Clica'm</button> </body> </html>
S’entén que el codi HTML ha de localitzar el fitxer JS. En aquest cas els dos fitxers estan en el mateix directori. És habitual que els fitxers JavaScript estiguin en una carpeta js/ que penja del directori principal de l’aplicació. En aquest cas quedaria:
<script src="js/fitxerExtern.js"></script>
Quan es fan aplicacions web s’ha de tendir a la separació del contingut, el disseny i la funcionalitat. Això s’aconsegueix fent que el codi HTML sigui el més net possible. Tota la part de JavaScript ha d’anar a fitxers externs, de la mateixa manera que tot el disseny i estil van a fitxers externs CSS. Fixeu-vos en la sentència:
<button type="button" onclick="funcio()">Clica'm</button>
La definició d’un botó HTML és la que defineix el comportament d’un event amb una funció JavaScript. Aquí també estem barrejant HTML amb JavaScript, i també cal evitar-ho.
És molt recomanable ser ordenats a l’hora de posar un nom als fitxers, versionant els fitxers. Fixeu-vos que en aquest primer exemple hem provat el codi de tres maneres diferents, i podeu identificar amb un nom diferent cadascuna de les proves.
Separació del codi en mòduls
Donat que les aplicacions en JavaScript van anar augmentant en complexitat, van aparèixer biblioteques especialitzades per carregar mòduls com AMD i CommonJS. Aquests mòduls contenien guions que podien carregar-se fent servir les paraules clau import
i export
per accedir a diferents funcionalitats. D’aquesta manera, es podien carregar només els mòduls necessaris i reutilitzar-los en diferents aplicacions.
Aquesta és una de les funcionalitats que s’han afegit a JavaScript en les darreres versions, de manera que és possible dividir les aplicacions en mòduls utilitzant import
i export
.
Càrrega de mòduls de forma local
No és possible carregar mòduls quan s’obre un fitxer HTML des del sistema de fitxers, és imprescindible treballar amb un servidor web local o un editor que tingui la capacitat de crear servidors web. Per exemple, Aptana Studio 3 permet llençar l’aplicació fent servir un servidor intern seleccionant “Firefox / Internal Server” quan es llença l’aplicació.
Per poder treballar amb mòduls cal indicar a la etiqueta <script>
el tipus module
de la següent manera:
<script type="module"> // Codi de la aplicació </script>
El següent exemple mostra el codi HTML per carregar el mòdul salutacions.js, que exporta les funcions hola
i adeu
:
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>El meu primer exemple amb mòduls</title> </head> <body> <h1>El meu primer exemple amb mòduls</h1> </body> <script type="module"> import {hola} from './salutacions.js'; import {adeu} from './salutacions.js'; hola(); adeu(); </script> </html>
I aquest és el codi corresponent al mòdul salutacions.js
:
export function hola() { mostrarAlerta("Hola món!"); } export function adeu() { mostrarAlerta("Adeu món!"); } function mostrarAlerta(text) { alert(text); }
S’ha de tenir en compte que l’àmbit de cada mòdul és el mateix mòdul, al contrari del que passa quan es carrega un fitxer JavaScript (en aquest cas, l’àmbit és global). És a dir, des d’un mòdul només es pot accedir al codi declarat al mateix mòdul, a l’espai global i a les funcions, classes i variables importades d’altres mòduls.
Fixeu-vos que la funció mostrarAlerta()
no és exportada des del mòdul i, per consegüent, es produirà un error si s’intenta cridar des del fitxer HTML.
Per aquest mateix motiu tampoc és possible accedir a les funcions importades des de les crides en línia com <button type=“button” onclick=“hola()”>Hola</button>
. Aquest tipus de crides es fan des de l’espai global i, per tant, cal afegir els mòduls exportats a l’espai global si volem utilitzar-les d’aquesta manera. Una possible implementació seria la següent:
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>El meu segon exemple amb mòduls</title> </head> <body> <h1>El meu segon exemple amb mòduls</h1> <button type="button" onclick="hola()">Hola</button> <button type="button" onclick="adeu()">Adeu</button> </body> <script type="module"> import {hola} from './salutacions.js'; import {adeu} from './salutacions.js'; window.hola = hola; window.adeu = adeu; </script> </html>
Com es pot apreciar, no ha estat necessari modificar el mòdul salutacions.js sinó que s’ha reaprofitat.
Una altra característica important dels mòduls és que el codi només és avaluat la primera vegada que s’importa, encara que sigui importat en múltiples llocs.
En resum, les característiques dels mòduls són les següents:
- Faciliten l’estructuració i reutilització del codi.
- Només es poden importar mòduls des d’altres mòduls.
- L’àmbit d’un mòdul és el mateix mòdul.
- No es poden carregar mòduls fent servir el sistema de fitxers, cal utilitzar un servidor web o un editor amb aquesta capacitat.
- El codi d’un mòdul només s’avalua la primera vegada que s’importa.
- Cal indicar amb
export
les funcions, classes i variables a exportar d’un mòdul; només aquestes estaran disponibles per importar. - Per importar un element d’un mòdul es fa servir la paraula clau
import
.
JavaScript: programació dirigida per esdeveniments
La programació dirigida per esdeveniments (event-driven programming) és un paradigma de programació en què el flux del programa està determinat per esdeveniments (events) com ara accions de l’usuari (típicament, moviments del ratolí i ús del teclat). Aquest és precisament el model que es troba en la programació web i en el seu resultat: la navegació per pàgines web.
En una pàgina web tenim un contingut estàtic, informatiu, però aquest contingut canvia a mesura que interactuem amb la pàgina web. Quan naveguem per la web, contínuament estem llençant events al motor de JavaScript, que s’encarrega de processar-los. Cliquem sobre un enllaç, fem scroll per la pàgina, passem el ratolí per damunt d’un menú desplegable… Tota la interacció que fem amb la pàgina web és recollida pel processador d’events que executa a temps real trossos de codi JavaScript que modifiquen el contingut de la pàgina. Per tant, les pàgines web esdevenen interactives, i el seu contingut és interactiu. Són els conceptes d’hipermèdia i hipertext, en què el consum d’informació no és lineal, sinó que és l’usuari qui decideix com interactua amb l’aplicació.
En el següent exemple estem associant al botó l’event onclick
, de manera que quan cliquem sobre el botó s’executa una sentència JavaScript:
Llistat complet d''events'
Podeu trobar una llista completa dels events disponibles al següent enllaç: mzl.la/3eLjWRB.
<button onclick="this.innerHTML=Date()">L'hora és ...</button>
this
fa referència al propi botó, i té la propietat innerHTML
que representa l’etiqueta que mostra el botó. El contingut d’aquesta etiqueta canvia pel valor que retorna la funció Date()
, que és l’hora del sistema.
Podeu provar el darrer exemple a: codepen.io/ioc-daw-m06/pen/YWZZPE
En aquest exemple fem servir l’event onmouseover
associat a la capa (div
) que conté dos paràgrafs:
<div onmouseover="document.getElementById('p1').innerHTML='Curs de JavaScript';document.getElementById('p2').innerHTML='IOC (Institut Obert de Catalunya)'"> <p id="p1">Primer paràgraf</p> <p id="p2">Segon paràgraf</p> </div>
Quan passem el ratolí per sobre la capa, canvia el contingut dels dos paràgrafs.
Podeu provar el darrer exemple a: codepen.io/ioc-daw-m06/pen/xOqqwR
Si us hi fixeu bé, en els dos exemples mostrats estem barrejant el llenguatge HTML amb sentències JavaScript. Tot i que és usual, la tendència serà separar totalment la part d’HTML de la part de JavaScript.
Eines i entorns per al desenvolupament web
Per programar amb JavaScript es necessita un editor de text. Molts llenguatges de programació es troben suportats amb IDE (Integrated Developement Environment) per tal d’escriure codi més ràpid, accedir a la documentació i evitar errors. En el cas de JavaScript creiem que l’aproximació és diferent. No importa tant l’editor de text que es fa servir, però sí que és necessari un entorn on el programador pugui provar de forma ràpida i còmoda el codi, i pugui depurar els errors. Sortosament, els navegadors web (Mozilla Firefox, Google Chrome) ja porten incorporats de forma predeterminada entorns de desenvolupament que ajuden a la programació i depuradors.
Així doncs, per desenvolupar aplicacions amb JavaScript fareu servir un editor de text a escollir, i les eines de desenvolupament integrades en el navegador. En els dos casos haureu d’esmerçar temps a trobar les dreceres de teclat, opcions i possibilitats per treure’n el màxim profit i rendiment, amb la idea de desenvolupar ràpid, i també corregir i depurar els errors amb rapidesa. Així mateix, és recomanable en la mesura del possible treballar amb dues pantalles: una per veure el codi (codi HTML, JavaScript, CSS, eines de depuració…) i una altra per veure el resultat.
Quant als editors, de text tenim diferents opcions. D’un editor de text adaptat al desenvolupament esperem que tingui les següents característiques:
- Elecció de llenguatge de programació. En el vostre cas, JavaScript.
- Coloració sintàctica.
- Autocompleció.
- Eines avançades de cerca i reemplaçament.
- Marcadors (bookmarks). Navegació pels marcadors.
- Autotabulació.
- Agrupament (i desagrupament) de codi.
- Noves funcionalitats amb instal·lació d’extensions (plugins).
Entre els editors que podeu fer servir destaquem:
- Sublime (multiplataforma)
- Notepadqq (Linux)
- Notepad ++ (Windows)
- Aptana Studio (multiplataforma)
- Brackets (multiplataforma)
Una altra possibilitat és utilitzar un IDE, com ara NetBeans o Eclipse. En el cas de desenvolupar per JavaScript, això només està justificat si coneixeu aquestes eines amb profunditat i fluïdesa perquè les utilitza amb d’altres llenguatges de programació.
Us podeu registrar en la plataforma codepen.io, encara que sense fer-ho també podeu editar directament els exemples, però sense desar-los.
De la mateixa manera necessitem entorns per fer proves del nostre codi. El codi JavaScript s’emmarca dins una pàgina web, tanmateix, en comptes de fer una pàgina web cada vegada que vulguem provar codi, hi ha entorns en línia que ens ajudaran a provar el codi de forma ràpida, com ara codepen.io.
S’utilitza codepen.io al llarg de tot el mòdul per tal que pugueu comprovar directament el funcionament de molts dels exemples.
Sintaxi del llenguatge JavaScript
JavaScript està basat en el estàndard ECMA-262, que defineix el llenguatge de propòsit general ECMAScript. Ecma International és una associació de la indústria fundada l’any 1961, i dedicada a la estandarització dels sistemes de la informació. Podeu consultar la pàgina web oficial a www.ecma-international.org, i des de ja.cat/jD3X5 descarregar-vos tot el document en format PDF o HTML.
A partir de l’edició de l’estàndard ECMA-262 del mes de juny del 2015, les actualitzacions a l’estàndard es publiquen anualment. Sobre aquest document trobareu una activitat a la part web del mòdul.
Sentències, comentaris, variables
JavaScript és un llenguatge de programació, és a dir, amb el seu codi d’instruccions podem fer programes informàtics. Aquests programes estaran orientats a fer pàgines web funcionals, atractives i interactives. Un programa es composa d’una llista d’instruccions que seran executades, en aquest cas interpretades, per l’ordinador. Aquestes instruccions són les sentències.
La importància d'utilitzar el punt i coma
A JavaScript, el punt i coma (;) indica el final d’una sentència. A JavaScript, posar-lo és opcional, però és molt convenient afegir-lo al final de les sentències perquè, en cas contrari, és el navegador qui els “posa” on creu convenient i pot no coincidir amb els nostres desitjos o suposicions.
Una cosa important, i que l’usuari aprèn de seguida, és que JavaScript distingeix entre majúscules i minúscules.
Així doncs, ja podeu fer el vostre primer programa i integrar-lo dins el codi HTML d’una pàgina web:
<html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>El meu primer programa amb variables</title> </head> <body> <h1 id="capcalera">El meu primer programa amb variables</h1> <script> //declarem una variable let x = 3; /* Els comentaris també poden ser multilínia. Declarem una altra variable */ let y = 5; let z = x * y; </script> </body> </html> <html>
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/bGEOmKN.
En aquest programa hem declarat les variables x, y, z. Declarem les variables amb la paraula reservada let
. A les dues primeres els hem donat un valor, i la tercera l’hem declarada com a producte de les dues primeres.
Actualment a JavaScript es poden declarar les variables fent servir tres paraules clau diferents:
let
: és la forma més habitual de declarar les variables en aplicacions de JavaScript modern. L’àmbit de la funció és de bloc, és a dir, si es declara entre claus{}
el seu valor és accessible només dintre d’aquest bloc.const
: l’àmbit és el mateix que l’anterior, però prohibeix sobreescriure el valor i, per consegüent no s’utilitza amb variables sinó amb constants.var
: l’àmbit de la variable és la funció on es declara o l’espai global si no es declara dins de la funció, sense importar si es troba dins d’un bloc o no. Aquesta era l’única forma de declarar les variables en ES5, i actualment no es recomana el seu ús.
Encara que no es produeix cap error si no es declaren les variables, és important fer-ho, perquè en cas contrari el navegador considera que es tracta d’una variable global, sense importància del context on es troba.
Normes sobre les variables
Les variables han de ser identificadors únics, i han de complir les normes següents:
- Només poden contenir lletres, dígits, signe de subratllat (_) i signe de dòlar ($).
- Han de començar amb una lletra, o bé amb el signe de subratllat (_) o el signe de dòlar (%%$%).
- Es distingeix entre majúscules i minúscules (la variable
x
és diferent de la variableX
). - No es poden utilitzar les paraules reservades de JavaScript (
while
,for
,next
…).
A més, hem comentat el codi, la qual cosa sempre està bé, mai és sobrer. Aquest programa ja fa alguna cosa, però l’usuari espera veure el resultat del producte. De fet, en JavaScript sempre tenim diferent alternatives. Per veure el resultat, podem fer-ho de quatre maneres diferents:
- Escrivint una caixa de text amb window.alert().
- Utilitzant un element HTML com ara un paràgraf.
- Escrivint directament en el document amb document.write().
- Escrivint a la consola del navegador amb console.log().
I sense proposar-nos-ho, aquí introduïm el concepte d’objecte: window
, document
i console
són objectes que tenen els mètodes alert()
, write()
i log()
. I és que JavaScript també és un llenguatge orientat a objectes, i contínuament haurem de parlar de mètodes i propietats dels objectes.
Aleshores el nostre codi pot quedar de la següent manera:
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>El meu primer programa</title> </head> <body> <h1 id="capcalera">El meu primer programa</h1> <script> //declarem una variable let x = 3; /* Els comentaris també poden ser multilínia. Declarem una altra variable */ let y = 5; let z = x*y; document.getElementById("capcalera").innerHTML = z; console.log(z); window.alert(z); document.write(z); </script> </body> </html>
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/mdVaQyb?editors=1000.
Com que estem en les primeres proves, us recomanem crear un fitxer HTML (per exemple, primerPrograma.html) amb l’anterior codi, i executar-lo. Per tal de veure la consola del navegador, en el navegador s’ha d’anar a les eines de desenvolupament i veure quina és la combinació de tecles adient. Depèn de si es fa servir Google Chrome o Mozilla Firefox, i fins i tot depèn de la versió d’aquests programes.
Fixem-nos que l‘script
és a la part de sota del body
. Si el pugem a dalt de tot del body
o al head
, a la consola obtindrem un missatge d’error:
TypeError: document.getElementById(...) is null
El programa no sap què és id=“capcalera” fins que no s’ha carregat la línia HTML <h1 id=“capcalera”>. Sempre s’ha de tenir ben present que el navegador web carrega la pàgina HTML línia a línia des del principi, i que no podem fer referència a un objecte o element HTML que encara no hem carregat en memòria. Una manera de resoldre aquest conflicte és utilitzar el següent codi, que fa ús de l’event onload()
:
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>El meu primer programa</title> </head> <script> function carregar() { //declarem una variable let x = 3; /* Els comentaris també poden ser multilínia. Declarem una altra variable */ let y = 5; let z = x*y; document.getElementById("capcalera").innerHTML = z; console.log(z); window.alert(z); document.write(z); } </script> <body onload="carregar()"> <h1 id="capcalera">El meu primer programa</h1> </body> </html>
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/ZEBKBGp?editors=1001.
Només quan s’ha carregat tota la pàgina HTML s’executa la funció JavaScript carregar(), que ara sí que ja podem definir en el head
, la qual cosa fa que el codi sigui més ordenat.
En aquest segon cas haureu vist que la sortida per pantalla és diferent (només hi ha un valor del 15), i que la consola està buida. Això és degut a què utilitzar document.write() un cop s’ha carregat totalment el document HTML fa que s’esborri la sortida HTML (document.write s’acostuma a utilitzar només per fer proves).
En aquest exemple hem utilitzat el mètode getElementById()
de l’objecte document
. Aquest mètode, molt útil, ens permet fer una cerca de l’element HTML que té per id=“capcalera”.
Fixem-nos que, pretenent fer una petita introducció a les sentències i variables, hem hagut d’introduir diversos conceptes com ara objectes, mètodes, fins i tot l’event onload()
.
Us podeu registrar i practicar en aquesta plataforma, tot i que també podreu fer proves amb els exemples tot editant-los i provant-los directament en el navegador.
Tipus de dades
A JavaScript les variables poden emmagatzemar molts tipus de dades, incloent-hi números, cadenes de text, objectes i funcions:
let dies = 365; let cadena1 = "curs de JavaScript"; let cadena2 = 'IOC'; let objecte = {nom: 'Joan', edat: 23}; let hola = function() {alert('Hola món!');};
Observeu que els números van sense cometes, i les cadenes de text van entre cometes simples o dobles. Si posem un número entre cometes, el tracta com una cadena de text.
let dies = '365'; let cad = "L'any té " + dies + " dies";
En l’anterior exemple podeu veure per què és útil que les cadenes es puguin emmarcar entre cometes dobles o simples, tot i que també es pot utilitzar el caràcter d’escapament (contrabarra, ‘\’):
let dies = '365'; // es tracta d'una cadena de text let cadena = 'L\´any té ' + dies + ' dies';
Cal tenir en compte que la concatenació de cadenes fa la conversió automàtica de nombres a cadenes de text sí que és possible. Per exemple:
Utilització de cometes dobles o simples
En general no hi ha diferència entre utilitzar cometes dobles o simples quan es treballa amb cadenes de text. Si la cadena de text conté cometes dobles, es recomana fer servir les cometes simples per estalviar-nos d’escapar les cometes dobles, i viceversa.
let dies = 365; // es tracta d'un nombre let cadena = 'L\´any té ' + dies + ' dies';
Una altra manera de construir cadenes de text és utilitzar plantilles de literals o template literals, en anglès (anteriorment conegudes com a plantilles de cadenes de text o template strings), en lloc de concatenar cadenes de text amb variables:
let dies = 365; // es tracta d'un nombre let cadena = `L\´any té ${dies} dies`; // plantilla de literal
Les plantilles de literals es troben entre accents greus (`) i permeten la interpolació de variables escrites entre claus precedides per un signe de dòlar: ${variable}
.
La utilització d’aquestes plantilles facilita la llegibilitat i la interpolació de variables i respecta els salts de línia dintre de la plantilla, com es pot apreciar en el següent exemple:
let dies = 365; let mesos = 12; let cad = `L\´any té: ${dies} dies ${mesos} mesos`;
Després de declarar una variable, aquesta no té valor (o diem que té valor undefined
). És el que passa en el següent exemple:
let color; console.log(color);
Per donar valor a una variable, utilitzem l’operador d’assignació ('=’):
color = 'blau';
Podem declarar moltes variables en una sola sentència:
let color1 = 'vermell', color2 = 'taronja', color3 = 'groc';
A continuació podeu veure un exemple complet d’utilització de variables:
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>Tipus de dades</title> </head> <script> console.clear(); let dies = 365; let curs = "curs de JavaScript"; let ioc = 'IOC'; console.log (curs); console.log (ioc); let cadCometesDobles = "L'any té " + dies + " dies"; console.log (cadCometesDobles); let cadCometesSimples = 'L\´any té ' + dies + ' dies'; console.log (cadCometesSimples); let cadPlantillaLiteral = `L´any té ${dies} dies`; console.log (cadPlantillaLiteral); let color; console.log(color); // undefined color = 'blau'; console.log(color); // blau let color1 = 'vermell', color2 = 'taronja', color3 = 'groc'; console.log (color1); console.log (color2); console.log (color3); </script> <body> <h1>Tipus de dades</h1> </body> </html>
</code>
Podeu provar el darrer exemple a: codepen.io/ioc-daw-m06/pen/qBbLQjw?editors=1001
L’onzena edició d’ECMAScript defineix nou tipus:
Quan proveu aquest exemple podeu utilitzar tant la consola integrada en el codepen com la consola del vostre navegador web.
Tipus de dades
Es recomana consultar el lloc web de Mozilla en anglès per veure els tipus de dades definits (mzl.la/3hiYYLy), ja que en altres llocs web pot ser que la llista no estigui actualitzada a l’última edició del llenguatge.
Enumeracions
Les enumeracions s’utilitzen per associar valors únics i constants d’una manera entenedora. Exemples: els colors d’un semàfor, les opcions d’una enquesta o els punts cardinals.
- Sis tipus de dades que són primitius (diferenciats per l’operador
typeof
):- undefined: variable a la qual no s’ha assignat cap valor o no declarada.
- boolean: cert o fals.
- number: nombres enters i reals, incloent-hi alguns valors especials com
infinity
. - string: cadenes de text.
- bigInt: permet operar amb nombres enters de mida arbitrària, no està limitada la mida com passa al tipus
number
. Cal afegirn
al final d’un nombre per convertir-lo enBigInt
. Fixeu-vos que un nombre no ha de ser obligatòriament “gran” per declarar-lo com aBigInt
. Pot aplicar-se a qualsevol valor enter, per exemple:let x = 42n
. - Symbol: és un tipus especial que permet crear funcionalitats similars a les enumeracions d’altres llenguatges, ja que cada
symbol
que es crea es garanteix que es tracta d’un valor únic.
- null: es tracta d’un valor especial. L’operador
typeof
el considera unobject
i és el valor retornat per algunes funcions quan el resultat no és vàlid, a diferència d‘undefined
, que és el valor d’una variable a la qual no s’ha assignat cap valor. - Object: tipus assignat a gairebé qualsevol element instanciat mitjançant la paraula clau
new
(array, map, set, date, etc.), així com a objectes declarats com a literals (per exemple,{nom: ‘Joan’, edat:‘23’}
). - Function: tipus assignat a les funcions.
Si executeu el següent exemple, podeu veure a la consola el valor retornat per l’operador typeof
per a cadascun d’aquests tipus:
let activat = true; console.log(`Activat: ${activat}. Tipus: ${typeof activat}`); let tipusNul = null; console.log(`Tipus nul: ${tipusNul}. Tipus: ${typeof tipusNul}`); let tipusSenseDefinir; console.log(`Tipus sense definir: ${tipusSenseDefinir}. Tipus: ${typeof tipusSenseDefinir}`); let nombre = 42; console.log(`Nombre: ${nombre}. Tipus: ${typeof nombre}`); let nombreEnterGran = 42n; console.log(`Nombre enter gran: ${nombreEnterGran}. Tipus: ${typeof nombreEnterGran}`); let cadena = "Hola món!"; console.log(`Cadena: ${cadena}. Tipus: ${typeof cadena}`); let simbol = Symbol("Simbol"); console.log(`Simbol: ${simbol.description}. Tipus: ${typeof simbol}`); let objecte = {nom: 'Joan', edat: 23}; console.log(`objecte: ${objecte.nom}, ${objecte.edat}. Tipus: ${typeof objecte}`); let funcio = function() {return 'funció cridada!'}; console.log(`Retorn de la funció: ${funcio()}. Tipus: ${typeof funcio}`);
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/JjGwxNO?editors=0012.
Utilitzant plantilles de literals és possible interpolar no només valors de variables sinó també propietats d’objectes, i inclús els valors retornats per funcions.
Embolcall automàtic de valors primitius en objectes
Cal destacar que JavaScript embolcalla els valors dels tipus primitius boolean
, string
i number
automàticament en objectes quan accedim a mètodes i propietats.
Per exemple, l’objecte Boolean
implementa el mètode toString()
, que converteix el valor en la cadena true
o false
. Així, doncs, quan escrivim true.toString()
, el que ocorre internament és que el valor true
s’embolcalla en un objecte de tipus Boolean i es crida a aquest mètode.
Conversió de tipus
Per operar amb diferents variables, JavaScript ha de conèixer el tipus de dades de les variables, i aleshores pot aplicar les seves regles internes.
Per exemple, en JavaScript és vàlid escriure:
let animal = "Àliga"; // String let numPotes = 2; // Number console.log (animal + numPotes);
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/dyGwEGd?editors=0012.
Això passa perquè internament numPotes
es converteix a cadena, que es concatena amb la variable animal
.
A JavaScript les expressions s’avaluen d’esquerra a dreta. Aquestes dues sentències donen resultats diferents:
let animal = "Àliga"; // String let numPotes = 2; // Number console.log (numPotes + numPotes + animal); // 4Àliga console.log (animal + numPotes + numPotes); // Àliga22
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/mdVaZXV?editors=0012.
En el primer cas s’han sumat els valors de numPotes
i, a continuació, s’ha concatenat “Àliga”
(Number + Number = tipus Number i després Number + String = tipus String), mentre que en el segon cas primer es produeix la concatenació de “Àliga”
i 2
, i a la cadena resultant es concatena el valor 2
.
Per la mateixa raó, si el valor d’una de les variables fos un nombre entre cometes (per exemple: “2”), el resultat seria la concatenació dels dos nombres en lloc de la suma:
let numPotes = 2; // Number let numCues = "1"; // String console.log (numPotes + numCues);
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/zYryQWd?editors=0012.
Per asegurar que el tipus és l’esperat, JavaScript ofereix la posibilitat de fer les següents conversions de tipus:
Boolean(valor)
: converteix el valor booleà.String(valor)
: converteix el valor en una cadena de text. Una altra opció és fer una concatenació del valor amb una cadena buida.Number(valor)
: converteix el valor en un nombre. Si el valor no és vàlid, el resultat seràNaN
(no és un nombre).parseInt(valor)
: converteix el valor en un nombre enter encara que es trobi un separador decimal.parseFloat(valor)
: converteix el valor en un nombre real.
let cadena = "3.1415"; let nombre = "42"; let nom = "Joan"; let aprovat = true; // Conversions a booleà console.log(Boolean(cadena)); // true console.log(Boolean(0)); // false console.log(Boolean("")); // false console.log(Boolean(null)); // false console.log(Boolean(undefined)); // false // Conversió a cadena console.log(String(nombre)); // "42" console.log(String(nombre) + nombre); // "4242" console.log(String(aprovat)); // "true" // Conversions a nombres console.log(Number(nom)); // NaN, no es un nombre console.log(Number(cadena) * 2); // 6.283 console.log(Number(aprovat)); // 1 console.log(parseInt(cadena)); // 3 console.log(parseFloat(cadena)); // 3.1415
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/bGEzBmG?editors=0012.
Cal tenir en compte que JavaScript no dona cap altra opció per fer conversions de tipus, però això no suposa cap inconvenient perquè és innecessari, ja que no hi ha restriccions als valors que es poden passar a les funcions i els tipus de les variables són dinàmics. És a dir, que la mateixa variable pot canviar de tipus de dades sense problema:
let z = 34; z = "ara soc una cadena"; z = true; z = undefined;
Podeu comprovar que aquest exemple és vàlid en l’enllaç següent: codepen.io/ioc-daw-m06/pen/bGEzBmG?editors=0012.
Tipus de dades compostes
Un tipus de dades compost és un tipus que permet emmagatzemar en una sola variable més d’un valor. A diferència d’altres llenguatges, a JavaScript les dades emmagatzemades poden ser de diferents tipus, ja que les variables són dinàmiques.
Originalment, a JavaScript només existien dos tipus de dades compostes: els arrays i els objectes.
Un array permet emmagatzemar múltiples valors en una variable i manipular-los mitjançant un índex.
let dies = ["dilluns", "dimarts", "dimecres", "dijous", "divendres", "dissabte", "diumenge"]; console.log(`Dia 3: ${dies[2]}`);
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/Bajveqa?editors=0012.
A la primera línia es declara un array assignant com a valor una llista de cadenes de text separades per comes entre claudàtors, i a la segona línia s’accedeix al valor emmagatzemat a la posició 2, que correspon al tercer valor perquè les posicions dels arrays comencen a comptar des de 0.
Històricament, a JavaScript s’han fet servir objectes com diccionaris de dades o mapes, que són uns tipus de dades compostes que admeten parells de valors, que són tractats com a parells propietat-valor:
var temperatures = {dilluns: 24, dimarts: 23, dimecres: 25, dijous: 20, divendres: 21, dissabte: 20, diumenge: 25}; console.log(`La temperatura de dijous va ser ${temperatures.dijous}ºC`);
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/QWyYGzy?editors=0012.
A diferència dels arrays, l’assignació literal d’aquest tipus es realitza envoltant els valors entre claus, separant els parells de valor i clau amb dos punts, i cada parell amb una coma. Per altra banda, per accedir als valors es fa servir el punt, indicant a l’esquerra el nom de la variable i a la dreta la clau. Aquest tipus de dades és l’origen del format d’intercanvi de dades JSON, que comparteix exactament la mateixa estructura.
Format JSON
El format JSON es tracta d’un format basat en la notació d’objectes de JavaScript molt utilitzat per enviar informació des de servidors a aplicacions o per crear fitxers que són llegits des del disc (per exemple, fitxers de configuració).
Cal destacar que encara que les claus no es trobin entre cometes simples ni dobles, internament es tracta de cadenes de text, ja que el nom de les propietats són de tipus string
. Això permet accedir a aquests valors indicant la clau com si es tractés d’una cadena de text fent servir claudàtors:
var temperatures = {dilluns: 24, dimarts: 23, dimecres: 25, dijous: 20, divendres: 21, dissabte: 20, diumenge: 25}; console.log(`La temperatura de dijous va ser ${temperatures["dijous"]}ºC`);
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/xxZMRMW?editors=0012.
Els arrays són una col·lecció de dades ordenades mentre que els objectes utilitzen claus per accedir i manipular els valors.
Actualment a JavaScript existeixen altres tipus de dades compostes:
- Map: és un diccionari de dades. És com utilitzar un objecte, però la clau pot ser qualsevol tipus i no només
string
(per exemple: nombres, booleans o inclús objectes). - Set: és un tipus especial per crear llistes sense elements duplicats que no dona accés a valors concrets. Permet afegir elements, comprovar si un element es troba al conjunt, eliminar elements i recórrer tot conjunt, però no accedir a valors concrets, perquè no s’assigna als valors ni un índex ni una clau.
Tipus: undefined i null
Els tipus undefined
i null
són molt similars, tots dos permeten alliberar un objecte (és decrementa el comptador de referències de l’objecte per indicar al recol·lector de brossa quan s’ha d’eliminar de la memòria) i s’avaluen com a fals quan fem operacions booleanes.
Recol·lector de brossa
El recol·lector de brossa (Garbage Collector en anglès) és un procés que periòdicament comprova les referències als objectes que es troben a memòria i quan no troba cap referència els elimina.
Una diferència important entre undefined
i null
és que tot i que els dos són tipus, l’operador typeof
retorna undefined
i object
respectivament, ja que null
es considera un objecte.
Habitualment no assignarem el valor undefined
, ja que aquest és el valor assignat automàticament pel llenguatge a les variables a les quals no s’ha assignat cap valor, als paràmetres de les funcions quan es criden sense haver passat tots els paràmetres o quan s’intenta consultar una propietat que no existeix a un objecte.
En canvi, el valor null
l’assignarem si és necessari alliberar una variable o quan sigui necessari indicar que no es vol assignar cap valor a la variable. Per exemple, perquè s’ha produït una entrada de valors errònia, perquè no s’ha pogut crear un objecte o perquè no s’ha trobat un objecte.
Un avantatge d’utilitzar el valor null
és que ens indica que s’ha assignat expressament, mentre que el valor undefined
no podem saber si ha estat assignat pel llenguatge (per exemple, perquè una variable o propietat no ha estat definida o perquè hem escrit malament el nom de la variable) o ha estat assignat expressament pel desenvolupador.
Tipus: Boolean
El tipus booleà només accepta dos valors: true
i false
(cert i fals). Aquest és el tipus que retornen les operacions comparatives de valors com: igual, major que, menor que, diferent, etc.
Cal distingir entre un valor de tipus booleà i un objecte de tipus booleà, ja que les instruccions condicionals tracten qualsevol objecte (a excepció de null
) com a cert. Podeu comprovar-ho amb el següent exemple:
// Assignem com a valor un objecte Boolean amb el valor false; let x = new Boolean(false); let y = false; console.log(`Valor de x: ${x}, tipus: ${typeof x}`); console.log(`Valor de y: ${y}, tipus ${typeof y}`); if (x) { console.log("Codi executat malgrat que x es fals"); } if (y) { // això no s'executa mai console.log("Codi executat malgrat que y es fals"); }
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/abdXWOB?editors=0012.
En canvi, si en lloc de fer servir l’operador new
es crida Boolean()
com a funció, el retorn serà un valor primitiu i el resultat serà l’esperat:
let x = Boolean(false);
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/YzwBVpa?editors=0012.
Tipus: Number i BigInt
A JavaScript només existeixen dos tipus de dades pels nombres:
- Number: utilitzat per a treballar amb nombres enters i reals:
- BigInt: afegit recentment al llenguatge per a treballar amb enters de mida arbitrària.
Els nombres normals estan limitats a una mida màxima, un cop superada aquesta mida el seu valor passa a ser Infinity
o -Infinity
. Per altra banda en treballar amb enters hi ha un límit a partir del qual les operacions no són correctes. Es poden consultar aquests valors amb Number.MAX_SAFE_INTEGER
, Number.MIN_SAFE_INTEGER
, Number.MAX_VALUE
i Number.MIN_VALUE
, com es pot comprovar en el següent exemple:
let limitRealA = Number.MAX_VALUE; // Valor máxim segur pels nombres reals let limitRealB = Number.MIN_VALUE; // Valor mínim segur pels nombres reals let limitRealExces1 = limitRealA + 1; let limitRealExces2 = limitRealA + 2; console.log(`Rang segur de nombres reals: [${limitRealB}, ${limitRealA}]`); console.log(`Limit superior de nombres reals + 1: ${limitRealExces1}`); console.log(`Limit superior de nombres reals + 2: ${limitRealExces2}`); console.log(`Són iguals els limits superior +1 i +2?: ${limitRealExces1 === limitRealExces2}`); console.log(`Diferència dels limits superiors +1 i +2: ${limitRealExces2 - limitRealExces1}`); console.log(`Quadrat del limit real superior: ${limitRealA ** 2}`); let limitEnterA = Number.MAX_SAFE_INTEGER; // Valor máxim segur pels nombres enters let limitEnterB = Number.MIN_SAFE_INTEGER; // Valor mínim segur pels nombres enters let limitEnterExces1 = limitEnterA + 1; let limitEnterExces2 = limitEnterA + 2; console.log('Rang segur de nombres enters: [${limitEnterB}, ${limitEnterA}]'); console.log('Limit superior de nombres enters + 1: ${limitEnterExces1}'); console.log('Limit superior de nombres enters + 2: ${limitEnterExces2}'); console.log('Són iguals els limits superiors d\´enters +1 i +2?: ${limitEnterExces1 === limitEnterExces2}'); console.log('Diferència dels limits superiors d\´enters +1 i +2: ${limitEnterExces2 - limitEnterExces1}'); console.log('Quadrat del limit enter superior: ${limitEnterA ** 2}');
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/LYGqLbM?editors=0012.
Com es pot apreciar, els resultats ja no són correctes un cop se superen els màxims: en incrementar en 1 i 2 els valors màxims no existeix cap diferència entre els resultats, sigui comparant-los o operant la diferència. Per altra banda, un cop el nombre és suficientment gran (com es pot veure en el cas del quadrat del límit real) el valor es torna infinit.
En alguns casos no es recomana treballar amb nombres reals (especialment quan es treballa amb diners), ja que els nombres reals afegeixen errors de precisions. Per exemple 0.1 * 0.2 = 0.020000000000000004
. Una alternativa és convertir els valors en enters multiplicant-los per una potència de 10, d’aquesta manera es conserven tots els dígits significatius.
Per a resoldre aquest problema, s’ha afegit al llenguatge el tipus BigInt, que permet treballar amb enters de qualsevol mida com es pot comprovar en el següent exemple:
let limitEnterA = Number.MAX_SAFE_INTEGER; // Valor máxim segur pels nombres enters let limitEnterB = Number.MIN_SAFE_INTEGER; // Valor mínim segur pels nombres enters let enterGranA = BigInt(Number.MAX_SAFE_INTEGER); // Valor máxim segur pels nombres enters let enterGranIncrement1 = enterGranA + 1n; let enterGranIncrement2 = enterGranA + 2n; console.log('Rang segur de nombres enters: [${limitEnterB}, ${limitEnterA}]'); console.log('Límit superior de nombres enters + 1n: ${enterGranIncrement1}'); console.log('Límit superior de nombres enters + 2n: ${enterGranIncrement2}'); console.log('Són iguals els limits superiors d´enters grans +1n i +2n?: ${enterGranIncrement1 === enterGranIncrement2}'); console.log('Diferència dels limits superiors d´enters grans +1n i +2n: ${enterGranIncrement2 - enterGranIncrement1}'); console.log('Quadrat de l´enter gran: ${enterGranA ** 2n}');
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/mdVvMPR?editors=0012.
Com es pot apreciar en executar-se aquest exemple, l’avaluació d’igualtat entre enterGranIncrement1
i enterGranIncrement2
dona fals i la diferència entre els dos valors és 1
, per consegüent el problema es resol correctament.
Fixeu-vos que un cop convertit el valor enter en BigInt només pot operar amb altres nombres de tipus BigInt, és a dir, 1n + 1
no és vàlid i es produeix un error.
Tipus: String
Les strings es poden posar entre cometes simples o dobles.
let animal = "àliga"; console.log (animal);
Les strings permeten fer el següent:
console.log (animal.length); console.log (animal.toUpperCase());
És a dir, l’string s’embolcalla automàticament en un objecte de tipus string
que té propietats i mètodes. Per tant es pot tractar com un objecte i, depenent de com es declari, el seu tipus serà String
o Object
:
let animal = "àliga" console.log (animal); console.log (typeof(animal)); let vegetal = new String("bleda"); console.log (vegetal); console.log (typeof(vegetal));
Aquesta segona forma, però, és més ineficient i, per tant, no la fem servir.
Tipus: Object
A JavaScript existeixen múltiples maneres de crear objectes. Actualment les dues més utilitzades són la instanciació a partir de classes mitjançant l’operador new
i la declaració literal. En el cas de la declaració literal d’objectes s’han de definir entre claus.
Els objectes es troben explicats amb més profunditat a la unitat “Objectes definits pel programador”.
Dels objectes en podem definir les propietats i els mètodes. Les propietats les escrivim amb parells nom:valor
, separat per comes. Per exemple:
let animal = { id : 345, nom : "Gos", classe : "Mamífers", familia : "Cànids" };
Hem definit quatre propietats de l’objecte animal, i podem accedir fàcilment als seus valors:
console.log (animal.id)
L’objecte “animal” té quatre propietats: id
, nom
, classe
, familia
. I com que és un objecte, també podem definir-ne mètodes. L’exemple més senzill seria:
let gos = { id : 345, nom : "Gos", classe : "Mamífers", familia : "Cànids", getTaxonomia : function() { return `${this.classe} - ${this.familia}`; } }; console.log (gos.getTaxonomia());
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/VwegMQd?editors=0012.
Com es pot apreciar, la creació d’objectes d’aquesta manera és molt concisa, però en cas de requerir crear més objectes amb la mateixa estructura (per exemple, un objecte que representi un gat) aquest sistema deixa de ser pràctic.
La instanciació a partir de classes, afegida a JavaScript en l’edició ES2015, soluciona aquest problema, ja que permet crear una classe que serveixi com a plantilla per generar totes les instàncies necessàries:
Classes en edicions anteriors
Les edicions anteriors de JavaScript permetien la creació d’objectes utilitzant un sistema basat en funcions constructores i prototips per simular la creació de classes. Tot i que aquest sistema continua sent vàlid, no es recomana utilitzar-lo, ja que és més enrevessat i en general no aporta cap avantatge.
class Animal { constructor(id, nom, classe, familia) { this.id = id; this.nom = nom; this.classe = classe; this.familia = familia; } getTaxonomia() { return `${this.classe} - ${this.familia}`; } } let gos = new Animal(1, 'Gos', 'Mamífers', 'Cànids'); let gat = new Animal(2, 'Gat', 'Mamífers', 'Fèlid'); console.log(gos.getTaxonomia()); console.log(gat.getTaxonomia());
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/RwrvZLV?editors=0012.
Fixeu-vos que un cop creada la classe és molt fàcil instanciar qualsevol quantitat d’elements mitjançant l’operador new
, indicant el nom de la classe i passant com a arguments l’identificador, el nom, la classe i la família. A més a més, no cal tornar a definir la funció getTaxonomia()
, ja que totes les instàncies la inclouen.
Tipus: Function
Una funció és un bloc de codi que rep uns valors d’entrada com a paràmetres, els processa i, opcionalment, en retorna un resultat. Per exemple:
Les funcions es troben explicades amb més profunditat a la unitat “Objectes definits pel programador”.
function hola(nom) { alert(`Hola ${nom}!`); } function suma(a, b) { return a + b; } hola('Joan'); console.log(suma(2, 2));
Per cridar a una funció només cal indicar el nom de la funció i, entre parèntesis, els arguments necessaris separats per comes.
A JavaScript les funcions són objectes de primera classe, és a dir, són tractades com qualsevol altre variable:
- Una funció pot ser assignada a una variable, i per aquest motiu també pot ser emmagatzemada en un array o un map.
- Es pot passar una funció com a argument a una altra funció.
- Una funció pot retornar una altra funció.
- Com que es tracta d’un objecte, té mètodes propis que poden ser cridats sobre aquest. Per exemple,
apply
,bind
, ocall
.
Cal tenir en compte que quan parlem d’objectes s’acostuma a parlar de mètodes, però també són funcions.
Per altra banda, a l’edició ES2015 es va incloure una sintaxi més compacta d’expressar les funcions anomenada “expressió de funció fletxa”, que és molt pràctica per passar com a argument en alguns casos. Aquest tipus de sintaxi no es pot utilitzar com a constructor ni es recomana utilitzar-la per definir mètodes.
Operadors
Els operadors serveixen per fer operacions entre dues o més variables. El primer operador que s’esmenta és el d’assignació. Després vénen els operadors aritmètics, que serveixen per fer operacions aritmètiques:
- Suma (+)
- Resta (-)
- Multiplicació (*)
- Divisió (/)
- Mòdul (%)
- Increment (++)
- Decrement (—)
let a = 100; a = ((a + 10 - 15 ) * 10 / 2 ) % 2;
Com a operador de cadena tenim la concatenació. Es representa amb el símbol +
en el sentit que estem sumant cadenes.
let cad1 = "curs de javaScript"; let cad2 = " de l'IOC"; let cad = cad1 + cad2; // curs de javaScript de l'IOC
Com que a JavaScript els tipus són dinàmics, si sumem números i cadenes, el més lògic és que el número passi a ser una cadena, per tal de poder fer una concatenació (al revés no té cap sentit):
console.log (a + cad); console.log (cad + a);
Operadors lògics i de comparació
Els operadors lògics i de comparació disponibles són:
- igual en valor que (
==
) - igual en valor i en tipus que (
===
) - no igual en valor que (
!=
) - no igual en valor o en tipus que (
!==
) - més gran que (
>
) - més petit que (
<
) - més gran o igual que (
>=
) - més petit o igual que (
< =
) - conjunció lògica (
&&
) - disjunció lògica (
||
) - negació lògica (
!
) - operador ternari (
?
)
Per evitar confusions sempre farem servir els operadors ===
i !==
en lloc de ==
i !=
, ja que la utilització d’aquests últims pot generar resultats inesperats com: 10 == “10”
o 0 == false
.
L’operador ternari (?
) mereix un tractament especial. La seva funció és clara amb els condicionals.
Assignació de valors i comparació lògica
No hem de confondre l’assignació (=
) amb la comparació lògica (==
). És un error freqüent en els principiants.
let a = 10; let b = 10; let c = 20; let d = "10"; console.log (a == c); // false console.log (a === b); // true console.log (a == d); // true console.log (a === d); // false console.log (a != c); // true console.log (a !== d); // true console.log (a > c); // false console.log (a < c); // true console.log (a >= b); // true console.log (a <= b); // true
La comparació entre cadenes es fa alfabèticament, com era d’esperar:
let cad1 = "avió"; let cad2 = "vaixell"; console.log (cad1 > cad2); // false
Si fem una comparació entre diferents tipus, com entre cadena i número, podem obtenir resultats inesperats. JavaScript converteix la cadena a número. Si la cadena és no-numèrica es converteix a NaN (Not a Number), que sempre serà fals. Si la cadena és buida, es converteix a 0.
let a = 10; let cad1 = "10"; let cad2 = "15"; let str = "cotxe"; console.log (a < cad2); // true. El '15' es converteix a 15 console.log (a == str); // false. La cadena 'cotxe' no es pot convertir a número console.log (cad1 < cad2); // true. Els dos són cadenes. S'ha de fer una comparació alfabètica. Com que el primer caràcter és el mateix, s'ha de mirar el segon caràcter.
Els exemples anteriors els podeu provar a: codepen.io/ioc-daw-m06/pen/KKVJQXw?editors=0012.
Els operadors de bit
Els operadors de bit (bitwise operators) no els veurem ja que difícilment es faran servir en el marc de la programació web. Si voleu aprofundir-hi, trobareu més informació a mzl.la/3s0JAsv
Operador de propagació
L’operador de propagació es representa com …
i s’utilitza per “propagar” els valors d’un array, de manera que els elements de l’array se “separen” com elements individuals. D’aquesta manera és possible passar un array propagat com argument a una funció que accepti múltiples arguments. Per exemple:
let dades = ['Joan', 27]; mostrarDades(...dades); function mostrarDades(nom, edat) { console.log(`${nom} té ${edat} anys`); }
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/gOPqvBy?editors=0012.
En aquest exemple el primer valor de l’array s’assigna al paràmetre nom
i el segon element, al paràmetre edat
.
Una altra aplicació de l’operador de propagació és treballar amb arrays, ja que propagant un array podem interpolar-los fàcilment i afegir tots els elements d’un array a un altre d’una tacada.
let colors = ['blau', 'vermell', 'groc']; let fruites = ['taronja', ...colors, 'llimona']; console.log(fruites); let nousColors = ['taronja', 'rosa', 'blanc', 'negre']; colors.push(...nousColors); console.log(colors);
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/wvMNyZv?editors=0012.
Fixeu-vos que a l’array fruites
al punt on s’ha propagat colors
s’han afegit tots els elements individualment.
En el segon cas es crida al mètode push()
de l’array colors, que serveix per afegir un element al final de l’array, però com que s’ha propagat nousColors
, en lloc d’afegir l’array com un únic element s’afegeix cada element de nousColors
, un a un.
Finalment, cal tenir en compte que hi ha un cas en què …
no és l’operador de propagació sinó l’operador Rest, que té el funcionament contrari, i el que fa és assignar a la variable a la qual s’aplica la “resta” de valors no assignats d’un array.
Es pot veure un exemple d’utilitació de l’operador Rest a la secció “Assignacions compostes” d’aquest mòdul.
Assignacions compostes
Els operadors d’assignació assignen valor a les variables. A part de l’operador igual ('='), a continuació podeu trobar una llista dels operadors compostos més utilitzats:
a += b
equival a a = a + ba -= b
equival a a = a - ba *= b
equival a a = a * ba /= b
equival a a = a / ba %= b
equival a a = a % b
Es pot consultar la llista completa d’operadors d’assignació compostos al següent enllaç: mzl.la/2ZPhwgp
Vegeu-ne un exemple de cada:
let a = 100; let b = 2; a += 10; // 110 console.log(a); a -= 15; // 95 console.log(a); a *= 10; // 950 console.log(a); a /= 2; // 475 console.log(a); // 475 a %= 2; // 1 (el mòdul és 1, doncs 475 és senar) console.log(a); // 1 (el mòdul és 1, doncs 475 és senar)
Proveu l’exemple anterior a: codepen.io/ioc-daw-m06/pen/vYLbWwL?editors=0012.
Una de les noves incorporacions a JavaScript és la sintaxi d’“assignació desestructurant”, que facilita l’extracció de dades d’arrays i les propietats d’objectes.
Per assignar els valors d’un array a la banda esquerra s’ha de posar entre claudàtors els noms de les variables a les quals s’assignaran els valors i a la banda dreta, l’array:
let a, b, altres; let valors = [10, 20, 30, 40, 50, 60]; [a, b, ...altres] = valors; console.log(a); // 10 console.log(b); // 20 console.log(altres); // [30, 40, 50, 60]
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/bGEzaOx?editors=0012.
Fixeu-vos que és possible utilitzar l’operador …
(en aquest context és l’operador Rest) a l’última variable, de manera que els valors no assignats de l’array s’assignen a aquesta variable.
Per altra banda, per assignar els valors de propietats a variables, cal posar tota l’assignació entre parèntesis. A la banda esquerra s’afegeixen les variables entre claus i a la banda dreta l’objecte del qual s’extrauran les propietats.
let nom, edat; let objecte = {nom: 'Joan', edat: 27}; ({nom, edat} = objecte); console.log(nom); // Joan console.log(edat); // 27
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/qBbgxdm?editors=0012.
Cal destacar que el valor de les variables només s’assignarà si es troba una propietat a l’objecte amb aquest mateix nom.
Decisions
Quan es programa, sovint cal prendre decisions i bifurcar el codi. Les instruccions condicionals ajuden a fer-ho. Les més habituals són if
, else
i else if
, d’una banda, i la clàusula switch
, d’altra banda.
Per altra banda, a JavaScript existeix una sintaxi de gestió d’errors que permet executar un bloc de codi o altre segons si es produeix un error o no, es tracta de la estructura try…catch
.
If, else, else if
S’usa if
per especificar un bloc de codi que s’executarà quan es compleixi una determinada condició:
let a = 99, b = 98; if ( a % 3 == 0) console.log(`${a} és divisible per 3`); if ( b % 2 == 0) { console.log(`${b} és divisible per 2`); console.log(`${b} és parell`); }
S’usa else
per especificar el bloc de codi que s’executarà quan la condició és falsa:
Cal utilitzar el sagnat (identation) dels blocs per tal que el codi sigui més llegible.
let a = 99; if ( a % 2 == 0) { console.log (`${a} és divisible per 2`); console.log (`${a} és parell`); } else { console.log (`${a} és senar`); }
S’usa else if
per especificar noves condicions a avaluar després que s’hagin avaluat les prèvies:
let b = 98; if ( b % 4 == 0) { console.log (`${b} és divisible per 4`); console.log (`${b} és parell`); } else if ( b % 3 ) { console.log (`${b} és divisible per 3`); } else if ( b % 2 ) { console.log (`${b} és divisible per 2`); console.log (`${b} és parell`); } else { console.log ('un altre cas'); }
Evidentment, l’avaluació de la condició pot ser més complicada:
let year = 2016; if ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))) { console.log (`L´any ${year} és un any de traspàs.\nPerò això no vol dir que tots els anys de traspàs siguin divisibles per 4.\nL´any 2000 no va ser un any de traspàs!`); document.write (`L´any ${year} és un any de traspàs.<br />Però això no vol dir que tots els anys de traspàs siguin divisibles per 4.<br />L´any 2000 no va ser un any de traspàs!`); } else { console.log (`L´any ${year} NO és un any de traspàs.`); document.write (`L´any ${year} NO és un any de traspàs.`); }
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/qBbgYaE?editors=0011
En el context de la consola, per fer una línia nova fem servir el metacaràcter \n
. Però en el context de la pàgina web cal fer servir <br />
.
Quan s’utilitzen plantilles de literals pot utilitzar-se un salt de línia dins del codi en lloc del metacaràcter \n
.
Per tal d’ampliar l’exemple anterior, mirarem si l’any actual és de traspàs, i després mirarem si un any aleatori entre 0 i 2500 també és de traspàs. Hem d’utilitzar els objectes Date
i Math
, que tots dos tenen la seva col·lecció de propietats i mètodes.
Recordeu que no es pot fer servir any
com a nom de variable perquè és una paraula reservada de JavaScript.
let d = new Date(); year = d.getFullYear(); if ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))) { console.log (`L´any ${year} és un any de traspàs. Però això no vol dir que tots els anys de traspàs siguin divisibles per 4. L´any 2000 no va ser un any de traspàs!`); document.write (`L´any ${year} és un any de traspàs.<br />Però això no vol dir que tots els anys de traspàs siguin divisibles per 4.<br />L´any 2000 no va ser un any de traspàs!`); } else { console.log (`L´any ${year} NO és un any de traspàs.`); document.write (`L´any ${year} NO és un any de traspàs.`); } document.write('<br>'); var num = 2500 * Math.random() year = parseInt(num); if ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))) { console.log (`L´any ${year} és un any de traspàs. Però això no vol dir que tots els anys de traspàs siguin divisibles per 4. L\´any 2000 no va ser un any de traspàs!`); document.write (`L´any ${year} és un any de traspàs.<br />Però això no vol dir que tots els anys de traspàs siguin divisibles per 4.<br />L\´any 2000 no va ser un any de traspàs!`); } else { console.log (`L´any ${year} NO és un any de traspàs.`); document.write (`L´any ${year} NO és un any de traspàs.`); }
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/wvMNjKr?editors=0011
Ara és el moment de fer programació estructurada i definir una funció per tal de reutilitzar el codi. A l’hora d’aprendre JavaScript, cal aplicar tots els conceptes de programació coneguts:
let year = 2016; mostrarInfo(year); let d = new Date(); year = d.getFullYear(); mostrarInfo(year); let num = 2500 * Math.random() year = parseInt(num); mostrarInfo(year); function mostrarInfo(year) { if ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))) { console.log (`L´any ${year} és un any de traspàs. Però això no vol dir que tots els anys de traspàs siguin divisibles per 4. L´any 2000 no va ser un any de traspàs!`); document.write (`L´any ${year} és un any de traspàs.<br />Però això no vol dir que tots els anys de traspàs siguin divisibles per 4.<br />L´any 2000 no va ser un any de traspàs!<br />`); } else { console.log (`L\´any ${year} NO és un any de traspàs.`); document.write (`L\´any ${year} NO és un any de traspàs.`); } }
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/rNxPvmg?editors=0011.
Switch
S’utilitza switch
quan tenim una sola expressió que s’ha de comparar moltes vegades, i només una és la correcta. En aquest exemple mostrem l’estació de l’any (primavera, estiu, tardor, hivern) en funció del mes, sense tenir en compte que els canvis d’estació es fan el dia 21 de març, 21 de juny, 21 de setembre i 21 de desembre:
let mes; let estacio; switch (new Date().getMonth()) { case 0: mes = "Gener"; estacio = "Hivern"; break; case 1: mes = "Febrer"; estacio = "Hivern"; break; case 2: mes = "Març"; estacio = "Hivern a primavera"; break; case 3: mes = "Abril"; estacio = "Primavera"; break; case 4: mes = "Maig"; estacio = "Primavera"; break; case 5: mes = "Juny"; estacio = "Primavera a estiu"; break; case 6: mes = "Juliol"; estacio = "Estiu"; break; case 7: mes = "Agost"; estacio = "Estiu"; break; case 8: mes = "Setembre"; estacio = "Estiu a tardor"; break; case 9: mes = "Octubre"; estacio = "Tardor"; break; case 10: mes = "Novembre"; estacio = "Tardor"; break; case 11: mes = "Desembre"; estacio = "Tardor a hivern"; break; default: console.log ("El codi mai passarà per aquí, però en altres casos podem utiltizar la clàusula default"); } console.log (`Mes: ${mes}`); console.log (`Estació: ${estacio}`);
Proveu l’exemple anterior a: codepen.io/ioc-daw-m06/pen/LYGqmjG?editors=0012.
Com que els symbols representen valors únics, també es poden utilitzar en un bloc switch:
// Definim els simbols pels possibles colors del semàfor let vermell = Symbol('vermell'); let groc = Symbol('groc'); let verd = Symbol('verd'); // Assignem el color actual del semàfor let colorSemafor = groc; let missatge; switch (colorSemafor) { case vermell: missatge = "no es pot passar!"; break; case groc: missatge = "compte!"; break; case verd: missatge = "es pot passar"; break; default: missatge = "Error, no s'ha assignat cap color al semàfor!" } console.log(`Color del semàfor: ${colorSemafor.description} - ${missatge}`);
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/ZEQworN?editors=0012.
Cal destacar que si no es posa break
en finalitzar un cas, es continuen executant casos un darrere l’altre fins que es troba un break
o es tanca el bloc switch.
Això pot ser útil en alguns casos, però és considerat una mala pràctica perquè pot generar confusions, ja que si un altre desenvolupador analitza el codi no pot saber si ha estat intencionat o es tracta d’un error.
En cas de voler aprofitar aquesta característica es recomana afegir un comentari de manera que quedi clara la intenció:
let estacio; switch (new Date().getMonth()) { case 0: // Caiguda intencionada case 1: estacio = "Hivern"; break; case 2: estacio = "Hivern a primavera"; break; case 3: // Caiguda intencionada case 4: estacio = "Primavera"; break; case 5: estacio = "Primavera a estiu"; break; case 6: // Caiguda intencionada case 7: estacio = "Estiu"; break; case 8: estacio = "Estiu a tardor"; break; case 9: // Caiguda intencionada case 10: estacio = "Tardor"; break; case 11: estacio = "Tardor a hivern"; break; default: console.log ("El codi no passarà mai per aquí, però en altres casos podem utiltizar la clàusula default"); } console.log (`Estació: ${estacio}`);
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/wvMNjNx?editors=0012.
Try...Catch
Quan JavaScript detecta un error, l’execució del bloc de codi queda interrompuda. Per evitar-ho podem gestionar aquests errors mitjançant els blocs try…catch
. Fixeu-vos en el següent exemple, on es crida una funció que no existeix:
La sintaxi dels blocs try…catch
també es troba en altres llenguatges com Java.
try { funcioInexistent(); console.log('Continua execució'); } catch (error) { // El contingut d'aquest objecte pot variar segons el navegador console.log(error); }
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/ZEQPzzz?editors=0012.
A la consola de Codepen no es mostra el contingut de l’objecte Error
.
Quan es crida la funció, com que no existeix s’interromp l’execució del codi. Per aquest motiu no es mostra mai el missatge de “Continua execució”, sinó que l’execució continua al bloc catch
, que rep com a paràmetre error
: un objecte de tipus Error
amb la informació del missatge.
Dins del bloc try
s’ha de posar el codi on pot produir-se l’error i al bloc catch
, el codi que es vol executar en cas que es produeixi algun error.
Addicionalment es pot afegir un bloc finally
, que serà executat tant si es produeix un error com si l’execució es realitza amb èxit. Vegeu-ho en el següent exemple:
try { funcioInexistent(); console.log('Continua execució'); } catch (error) { // El contingut d'aquest objecte pot variar segons el navegador console.log(error); } finally { console.log('Bloc executat'); }
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/LYGaPVW?editors=0012.
Bucles
Els bucles són necessaris quan es vol executar un codi un nombre determinat o indeterminat de vegades, potser fins que s’acompleixi una condició de sortida. Tenim diferents tipus de bucles:
for
: bucles que repeteixen el bloc de codi un nombre fix de vegades.for…in
: bucles que recorren les propietats d’un objecte o els elements d’un array.for…of
: bucle per recórrer elements iterables: array, map, set, l’objectearguments
, etc.Array.forEach
: bucle per recórrer i processar tots els elements d’un array.while
: bucles que repeteixen el bloc de codi mentre la condició de sortida sigui certa.do…while
: igual que l’anterior, però la condició de sortida s’avalua al final.
Bucles for, for...in i for...of
La sintaxi bàsica del bucle for
és:
for ( expressió1; expressió2; expressió3) { bloc de codi a executar; }
L´expressió1
s’avalua a l’inici; l´expressió2
és la condició de sortida del bucle; l´expressió3
s’executa a cada volta del bucle. Un exemple típic:
// taula de multiplicar del 6 let cad = ""; for (let i = 1; i <= 10; i++) { cad += "6 * " + i + " = " + 6*i + "\n"; } console.log(cad);
I amb dos bucles for
niats podem fer totes les taules de multiplicar de l’1 al 10:
// Taules de multiplicar de l'1 al 10 let cad; for (let j = 1; j <= 10; j++) { cad = ""; for (let i = 1; i <= 10; i++) { cad += `${j} * ${i} = ${j * i} \n`; } console.log(cad); }
Les tres expressions dins el for
són, de fet, opcionals. L’única cosa que hem de tenir en compte és de no provocar un bucle sense fi, la qual cosa penjaria el navegador web des del qual estem provant. Per tant, sempre hi haurà d’haver una condició de sortida, i podrem sortir del bucle amb la clàusula break
:
let i = 0; for (;;) { console.log(i); i++; if (i>15) break; }
Els exemples anteriors els podeu provar a: codepen.io/ioc-daw-m06/pen/xxZMzqd?editors=0012.
Per recórrer els elements d’un array també es pot fer servir el bucle for, ja que els índexs dels arrays són consecutius i, per consegüent, només cal recórrer els valors des de 0 fins a l’últim element de l’array, que correspondrà a la mida de l’array menys 1:
let colors = ['vermell', 'blau', 'groc'] for (let i = 0; i < colors.length; i++) { console.log(`Color: ${colors[i]}`); }
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/jOWdKaO?editors=0012.
Malauradament, aquest sistema no permet recórrer les propietats d’un objecte (per exemple, perquè s’ha utilitzat com una estructura de dades). Per iterar sobre les propietats d’un objecte cal utilitzar la sintaxi for…in
:
let persona = {nom: 'Joan', edat: 23, poblacio: 'Martorell'}; for (let clau in persona) { console.log(`${clau}: ${persona[clau]}`); }
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/LYGqrQe?editors=0012.
Fixeu-vos que el valor que s’assigna a clau
és el nom de la propietat; així doncs, per accedir al valor de la propietat a l’objecte, s’ha de posar la clau entre claudàtors: persona[clau]
.
També es pot fer servir for…in
amb arrays, però en aquest cas el valor de la variable correspondrà a l’índex.
Però cap d’aquests mètodes serveix per recórrer els elements iterables map i set. Per iterar sobre aquest tipus d’estructures cal fer servir for…of:
let llista = new Set(); llista.add('Joan'); llista.add('Maria'); llista.add('Pere'); llista.add('Rosa'); for (let noms of llista) { console.log(`Noms: ${noms}`); } let persones = new Map(); persones.set('Joan', 23); persones.set('Maria', 21); persones.set('Pere', 43); persones.set('Rosa', 52); for (let [nom, edat] of persones) { console.log(`Nom: ${nom}, edat: ${edat}`); }
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/vYLbrbQ?editors=0012.
Fixeu-vos que en el cas del mapa es requereixen dues variables: una per a la clau i una altra per al valor.
Com podeu observar, a diferència de for…in
, que s’assignava a la variable el nom de la propietat, en el cas de for…of
a la variable se li assigna el valor. Així doncs, és possible iterar sobre un array i obtenir directament els valors sense necesitat d’utilitzar un índex:
let colors = ['vermell', 'blau', 'groc'] for (let color of colors) { console.log(`Color: ${color}`); }
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/OJMdEGK?editors=0012.
Bucles Array.forEach
Aquest sistema d’iteració permet recórrer tots els elements d’un array i cridar una funció per a cadascun dels elements.
En versions anteriors de JavaScript la utilització d‘Array.forEach
era més rellevant perquè el bucle s’executava en un àmbit de funció que era l’únic respectat per les variables.
let colors = ['vermell', 'blau', 'groc'] colors.forEach(mostrarMissatge); function mostrarMissatge(color) { console.log(`Color: ${color}`); }
Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/yLeZqNM?editors=0012.
Cal destacar que l’única manera de sortir d’un bucle d’aquest tipus és llençant una excepció des de la funció.
Bucles while, do...while
El cas més senzill és el bucle while
. Mentre es compleixi la condició s’executarà el bloc de codi:
while (condició) { bloc de codi }
Un exemple que utilitza la funció charAt()
de les strings. Mostrem la part de la cadena str
fins que no trobem el caràcter i:
let str = "curs de javaScript de l'IOC"; let cad = ""; let i = 0; let res = ""; while (res != 'i') { res = str.charAt(i); cad += res; i++; } console.log(cad);
Una variant és el bucle do…while
. El bloc s’executa com a mínim una vegada. La condició de sortida s’avalua al final, i es va executant el codi fins que no es compleixi la condició de sortida.
do { bloc de codi } while (condició);
Per exemple,
let str = "curs de javaScript de l'IOC"; let cad = ""; let i = 0; let res = ""; do { res = str.charAt(i); cad += res; i++; } while (res != 'i') console.log(cad);
En aquest cas, tant se val avaluar la condició al principi o al final, el resultat és el mateix.
Podem sortir del bucle en qualsevol moment fent un break:
let str = "curs de javaScript de l'IOC"; let cad = ""; let i = 0; let res = ""; while (res != 'i') { res = str.charAt(i); if (res == 'a') break; cad += res; i++; } console.log(cad);
Proveu els exemples anteriors a: codepen.io/ioc-daw-m06/pen/oNbmMLJ?editors=0012.