Salta el contingut

UT04 — Estructures definides per l'usuari en JavaScript

Introducció

Aquesta unitat introdueix les estructures de dades, amb especial atenció als arrays. S'explicarà l'objecte Array: creació, recorregut, eliminació d'elements i les propietats i mètodes disponibles. També es presentaran diferents tipus d'arrays (paral·lels i multidimensionals).

S'introdueixen les funcions: declaració, paràmetres, abast de variables, funcions niuades i funcions predefinides del llenguatge. Finalment, s'exposarà la creació d'objectes definits per l'usuari amb propietats i mètodes propis.

1. Arrays

Conceptes bàsics

En els llenguatges de programació existeixen estructures de dades que permeten emmagatzemar informació més complexa que una variable senzilla. L'array és una de les estructures més utilitzades: és una zona d'emmagatzematge contínua que pot contenir diversos valors en lloc d'un només, com succeeix amb una variable simple.

Els arrays també s'anomenen matrius o vectors. Des d'un punt de vista lògic, una matriu es pot concebre com un conjunt d'elements ordenats en fila (o files i columnes si té més dimensions).

Tots els arrays es consideren, essencialment, unidimensionals, però els seus elements poden ser al seu torn altres arrays, cosa que dóna lloc a arrays multidimensionals (fàcilment imaginables en una, dues o tres dimensions). És preferible no abusar de dimensions superiors si la lògica del programa no ho requereix, ja que això dificulta la lectura i mantenibilitat.

Els arrays són adequats quan l'accés a les dades és aleatori. Si predominen accessos seqüencials o la necessitat de canviar la grandària amb freqüència, una llista podria ser una opció més adequada.

Cada element d'un array és referenciat per la seva posició, anomenada índex. En JavaScript l'indexació és de base zero; per tant, el primer element té índex 0.

Els arrays apareixen en JavaScript des de la versió 1.1; per tant, en els navegadors actuals no hi ha limitacions per emprar-los.

Indexació (definició): procés d'organització ordenada de la informació per a obtenir resultats més ràpids i rellevants durant les cerques.

1.1 Treballant amb arrays

Array.

Un array és una estructura per emmagatzemar i manipular col·leccions de dades. A diferència d'altres llenguatges, en JavaScript els arrays són molt versàtils pel que fa al tipus de dades que poden contenir: cada posició pot tenir elements de tipus diferent.

En programació, un array es defineix com una col·lecció ordenada de dades; s'assembla a una taula o una fulla de càlcul. JavaScript utilitza internament arrays per gestionar col·leccions del document HTML (p. ex. anchors, forms, links, images). Exemple d'accés al primer enllaç: document.links[0].

Quan es dissenya una aplicació, cal identificar quan convé usar arrays. Per exemple, una sèrie de coordenades geogràfiques d'una ruta constitueix un bon candidat per a un array.

Creació

Per crear un array hi ha diverses sintaxis:

let coches = [ 'Seat', 'Audi', 'BMW', 'Toyota' ];
let numeros = [ 1, 5, 3, 9, 6, 4 ];
let diferentes = [ 'Pepe', 5, 'Juan', false ];

També es pot usar new Array() i assignar elements mitjançant índex:

let coches = new Array();
coches[0] = 'Seat';
coches[1] = 'Audi';
coches[2] = 'BMW';
coches[3] = 'Toyota';

És possible crear arrays associatius (associacions clau-valor):

let edades = new Array();
edades['Juan'] = 20;
edades['Ana'] = 18;
edades['Pedro'] = 25;
console.log(edades);
console.log(edades['Juan']);
console.log(edades['Ana']);
console.log(edades['Pedro']);

Recorregut

Per recórrer un array podem utilitzar els bucles coneguts. Per exemple:

let numeros = [ 1, 5, 3, 9, 6, 4 ];

for (let i = 0; i < numeros.length; i++) {
    console.log(numeros[i]);
}

Altres constructes són:

for (let i in numeros) {
    console.log(numeros[i]);
}

numeros.forEach(numero => console.log(numero));

I també es poden emprar map, reduce i filter:

let cuadrados = numeros.map(numero => { return numero * numero });
console.log(cuadrados);

Per saber-ne més

Per exemples detallats de map, filter i reduce: https://code.tutsplus.com/es/tutorials/how-to-use-map-filter-reduce-in-javascript--cms-26209

Treballant amb elements

Afegir un element dinàmicament:

numeros[6] = 15;
console.log(numeros);

Si s'afegeix un element fora del final, les posicions intermèdies poden quedar amb valors indefinits.

Eliminar un element amb delete no redueix la longitud de l'array:

delete numeros[6];
console.log(numeros.length);    // 7
console.log(numeros[6]);        // undefined

Per reduir la mida cal usar splice (posició d'inici, nombre d'elements):

numeros.splice(5, 2);
console.log(numeros);           // [ 1, 5, 3, 9, 6 ]

Propietats i mètodes

Propietats de l'objecte Array:

Propietat Descripció
constructor Retorna la funció que va crear el prototype de l'array.
length Ajusta o retorna el nombre d'elements d'un array.
prototype Permet afegir propietats i mètodes a un objecte.

Mètodes de l'objecte Array:

Mètode Descripció
concat() Uneix dos o més arrays i retorna una còpia dels arrays units.
join() Uneix tots els elements d'un array en una cadena.
pop() Elimina l'últim element d'un array i el retorna.
push() Afegeix nous elements al final d'un array i retorna la nova longitud.
reverse() Inverteix l'ordre dels elements d'un array.
shift() Elimina el primer element d'un array i el retorna.
slice() Selecciona una part d'un array i retorna el nou array.
sort() Ordena els elements d'un array.
splice() Afegeix/elimina elements a un array.
toString() Converteix un array a una cadena i retorna el resultat.
unshift() Afegeix nous elements a l'inici d'un array i retorna la nova longitud.
valueOf() Retorna el valor primitiu d'un array.

Arrays paral·lels

Els arrays paral·lels són conjunts de dos o més arrays que comparteixen l'índex per referir-se a termes homologables. Exemple:

let personajes = [ 'Bob Esponja', 'Calamardo', 'Patricio' ];
let color = [ 'amarillo', 'beige', 'rosa' ];
for (let i = 0; i < personajes.length; i++) {
    console.log(`${personajes[i]} es de color ${color[i]}`);
}

Arrays multidimensionals

Un element d'un array pot ser un altre array; això genera arrays multidimensionals. Exemple:

let personajes = [ 
    [ 'Bob Esponja', 'amarillo'],
    [ 'Calamardo', 'beige' ],
    [ 'Patricio', 'rosa' ]
];
for (let i in personajes) {
    console.log(`${personajes[i][0]} es de color ${personajes[i][1]}`);
}

2. Funcions

Conceptes bàsics sobre funcions

Una funció és la definició d'un conjunt d'accions preprogramades. Les funcions es poden invocar mitjançant esdeveniments o crides des del script.

És recomanable dissenyar funcions reutilitzables perquè esdevinguin blocs constructius que accelerin el desenvolupament.

Si bé altres llenguatges distingeixen mètodes i procediments, en JavaScript s'utilitza el terme funció per a tots dos casos. Una funció pot retornar un valor amb return, però no sempre és obligatori.

Sintaxi

Sintaxi tradicional:

function nombreFuncion ( [parametro1]....[parametroN] ) {
     // instruccions
     [return valor]
}

Sintaxi amb operador fletxa (arrow function), recomanada actualment:

const nombreFuncion = ([parametro1]...[parametroN]) => {
      // instruccions
      [return valor]
}

Els noms de funció segueixen les mateixes restriccions que les variables i haurien de reflectir l'acció que realitzen (p. ex. chequearCorreo, calcularFecha).

Per invocar una funció:

nombreFuncion();  // Executa les instruccions de la funció.

Si la funció retorna un valor i el volem capturar:

variable = nombreFuncion();

Les funcions en JavaScript són objectes i, per tant, poden tenir mètodes com toString() que retornen el seu codi font.

Paràmetres

Els paràmetres (arguments) permeten passar dades a una funció. En la definició, s'indiquen els noms de les variables que rebran els valors.

Exemple:

const saludar = (nombre) => {
      alert(`Hola ${nombre}`);
}

saludar('Bob Esponja'); // Mostra alerta: "Hola Bob Esponja."

Els paràmetres són variables locals a la funció i s'inicialitzen quan s'invoca la funció.

Exemple d'una funció que retorna un valor:

const calcularMayor = (num1, num2) => {
    return (num1 > num2) ? num1 : num2;
}

console.log(calcularMayor(7, 5)); // 7

Àmbit de les variables

Hi ha variables globals (definides fora de qualsevol funció) i variables locals (definides dins d'una funció).

Una variable global en JavaScript té abast dins del document actual carregat; totes les instruccions (incloses les de dins de funcions) poden accedir i modificar-la. Quan la pàgina es tanca, aquestes variables s'eliminen.

Una variable local es defineix dins d'una funció; el seu abast està limitat a la funció i no és accessible fora d'aquesta.

Reutilitzar el mateix nom per a una variable global i una local pot generar errors subtils (bugs) perquè la variable local oculta la global. S'aconsella evitar aquesta pràctica.

Exemple:

let nombre = 'Pepe';
const prueba = () => {
    let nombre = 'Pedro';
    console.log(nombre);      // Pedro
}
console.log(nombre);          // Pepe
prueba();
console.log(nombre);          // Pepe

Funcions niuades

És possible declarar funcions dins d'altres funcions. Això permet encapsular funcionalitats que només tenen sentit dins del context de la funció principal i limitar l'abast (privadesa) d'aquestes funcions auxiliars.

Exemple:

const calcularHipotenusa = (cateto1, cateto2) => {
   const calcularCuadrado = (x) => { return x * x; }
   return Math.sqrt(calcularCuadrado(cateto1) + calcularCuadrado(cateto2));
}
console.log(calcularHipotenusa(1, 2)); // 2.23606797749979

Funcions predefinides (globals)

Les funcions globals són accessibles des de qualsevol lloc del codi i sovint permeten convertir tipus de dades o manipular cadenes i URLs. Alguns exemples:

Funció Descripció
decodeURI() Decodifica caràcters especials d'una URL excepte: , / ? : @ & = + $ #
decodeURIComponent() Decodifica tots els caràcters especials d'una URL.
encodeURI() Codifica caràcters especials d'una URL excepte: , / ? : @ & = + $ #
encodeURIComponent() Codifica tots els caràcters especials d'una URL.
escape() Codifica caràcters especials en una cadena (excepte * @ - _ + . /).
eval() Avalua una cadena i l'executa si conté codi.
isFinite() Determina si un valor és un número finit vàlid.
isNaN() Determina si un valor no és un número.
Number() Converteix un valor a nombre.
parseFloat() Converteix una cadena a nombre real.
parseInt() Converteix una cadena a enter.
unescape() Decodifica caràcters especials en una cadena (excepte * @ - _ + . /).

3. Objectes

Conceptes bàsics sobre objectes

Un objecte en JavaScript és una col·lecció de propietats. Les propietats poden ser dades, tipus, funcions (mètodes) o altres objectes. Un mètode és, en essència, una funció associada a un objecte i té accés a les propietats d'aquest objecte: és una peça clau en la programació orientada a objectes.

Definició de classe

Per definir una classe (plantilla per crear objectes), s'utilitza la sintaxi:

class NombreClase {
    // Cos de la classe
}

Per convenció, el nom de la classe comença amb majúscula.

Exemple inicial per a gestionar contactes:

class Contacto {
    // Cos de la classe Contacto
}

Creació d'instàncies

Per crear un objecte a partir d'una classe s'utilitza l'operador new:

let pepe = new Contacto();
let juan = new Contacto();

Propietats

Les propietats representen l'estat d'un objecte. Es poden declarar en el cos de la classe o, preferentment, inicialitzar-les en el constructor utilitzant this.

Exemple:

class Contacto {
    nombre;
    correo;
}
let juan = new Contacto();
console.log(juan); // Object { nombre: undefined, correo: undefined }
juan.nombre = 'Juan';
console.log(juan.nombre); // Juan

Constructor

El constructor és una funció especial que s'executa en crear una instància amb new. Normalment s'usa per inicialitzar propietats.

class Contacto {
    constructor(nombre, correo) {
        this.nombre = nombre;
        this.correo = correo;
    }
}

let pepe = new Contacto('Pepe', 'pepe@gmail.com');
console.log(pepe); // Object { nombre: "Pepe", correo: "pepe@gmail.com" }
let juan = new Contacto();
console.log(juan); // Object { nombre: undefined, correo: undefined }

Mètodes

Els mètodes són funcions definides dins la classe que determinen el comportament dels objectes:

class Contacto {

    nombre;
    correo;

    constructor(nombre, correo) {
        this.nombre = nombre;
        this.correo = correo;
    }

    saludar() {
        return `Hola, soy ${this.nombre} y mi correo es ${this.correo}`;
    }
}

let pepe = new Contacto('Pepe', 'pepe@gmail.com');
console.log(pepe.saludar()); // "Hola, soy Pepe y mi correo es pepe@gmail.com"

També hi ha mètodes estàtics, comuns a la classe i no a les instàncies, que es declaren amb la paraula clau static.

Herència

JavaScript permet crear jerarquies mitjançant extends. Una classe pot heretar estructura i comportament d'una altra:

class ContactoTelefonico extends Contacto {

    telefono;

    constructor(nombre, correo, telefono) {
        super(nombre, correo);
        this.telefono = telefono;
    }

    saludar() {
        return `${super.saludar()} y mi teléfono es ${this.telefono}`;
    }
}

let maria = new ContactoTelefonico('María', 'maria@gmail.com', '666112233');
console.log(maria.saludar()); // "Hola, soy María y mi correo es maria@gmail.com y mi teléfono es 666112233"