Esercizio 15: JS

Keywords: variabili, funzioni, eventi

Esercizio precedente Esercizio successivo

Rivediamo insieme:

Oggetti

Gli oggetti sono delle variabili che possono contenere più variabili.

const student = {type:"human", age:"25", name:"Giovanna", surname: "Rossi"};

Alcune variabili e.g. gli array possono anche contenere più oggetti e così via.

const students = [{name:"Giovanna", surname: "Rossi", age:"25", year:1}, {name:"Antonino", surname: "Caffaro", age:"25", year:2}, {name:"Roberto", surname: "Hu", age:"24", year:1};

Per accedere ad un oggetto possiamo usare sia la notazione con il punto che quella con le parentesi quadre, come per gli indici

Alcuni metodi:

  1. Object.keys(student);
student.name
= > "Giovanna"
student["surname"]
=> Rossi

Gli oggetti possono anche avere dei metodi, ovvero funzioni che si applicano sull'oggetto, come ad esempio ".lenght" per le array.

const student = {
    firstName: "Giovanna",
    lastName : "Rossi",
    id       : 00756565,
    fullName : function() {
        return this.firstName + " " + this.lastName;
    }
};
student.fullName
=> "Giovanna Rossi"

Possiamo anche creare gli oggetti tramite dei template, chiamati constructor. Sono utili in moltissimi contesti. Vediamo insieme i metodi principali.

Eventi

<button onclick="this.innerHTML=Date()">Che ore sono?</button>

Cos'è this?

this è una keyword particolare. Il suo valore dipende dal contesto in cui viene utilizzata, esattamente come "questo" nella nostra lingua.

  1. nel metodo di un oggetto, si riferisce all'oggetto stesso
  2. in un evento, this si riferisce all'elemento che ha ricevuto l'evento.

Funzioni e Callback

In JS, le funzioni vengono eseguite nell'ordine di chiamata.

Prendiamo ad esempio questa funzione.

function myDisplayer(some) {
document.getElementById("demo").innerHTML = some;
}

function myFirst() {
myDisplayer("Hello");
}

function mySecond() {
myDisplayer("Goodbye");
}

myFirst();
mySecond();

Creiamo un paragrafo con id="demo"

= > Risultato: "Buonasera"

function myDisplayer(some) {
document.getElementById("demo").innerHTML = some;
}

function Addiction(num1, num2) {
let sum = num1 + num2;
myDisplayer(sum);
}

function Subtraction(num1, num2) {
let sum = num1 - num2;
myDisplayer(sum);
}

Addiction(5, 5);
Subtraction(5, 5);

In questo caso possiamo vedere come le due funzioni di calcolo chiamano la stessa funzione che ha il solo ruolo di mostrare il risultato. Solo una volta eseguito il codice prima la funzione myDisplayer() verrà chiamata.

= > Risultato: 10

= > Risultato: 0

Sincroni e asincronia

In JS, ogni operazione viene eseguita subito dopo la precedente, passo dopo passo.

Possiamo però bypassare questo sistema con metodi asincroni.

function printNumbersSync() {
for (let i = 1; i <= 5; i++) {
    setTimeout(() => {
    console.log(i);
    }, i * 1000);
}
}
function printNumbersAsync() {
setTimeout(() => {
    console.log(1);
    for (let i = 2; i <= 5; i++) {
    console.log(i);
    }
}, 1000);
}

Nel caso di timeout, attese, caricamenti da altre fonti, abbiamo bisogno di attendere un certo risultato prima di partire col successivo.

Le promesse in JavaScript sono oggetti che rappresentano il completamento (o il fallimento) di un'operazione asincrona e consentono di gestire il risultato dell'operazione quando diventa disponibile. In pratica, quando si effettua una chiamata asincrona a una funzione o a un'API, non si sa quando la risposta sarà pronta. Invece di bloccare l'esecuzione del codice fino a quando la risposta non arriva, si può restituire un oggetto promessa che rappresenta il risultato futuro dell'operazione. Quando la risposta è disponibile, la promessa si risolve con un valore di successo, oppure viene rifiutata con un valore di errore. Per esempio, si può utilizzare una promessa per eseguire una chiamata AJAX per ottenere i dati da un server:

const promise = fetch('https://jsonplaceholder.typicode.com/todos/1');
    promise
      .then(response => response.json())
      .then(data => console.log(data))
      .catch(error => console.error(error));

In questo esempio, fetch restituisce una promessa che rappresenta il completamento dell'operazione di chiamata AJAX. La promessa viene quindi gestita con i metodi then e catch. Il metodo then viene chiamato quando la promessa si risolve con successo, e il metodo catch viene chiamato quando la promessa viene rifiutata.

L'oggetto Promise è un costruttore di oggetti utilizzato per rappresentare una "promessa" (o "promise" in inglese) di completamento di un'operazione asincrona. Un oggetto promessa rappresenta un'operazione che è in corso, ma la cui esecuzione potrebbe richiedere del tempo e/o potrebbe essere soggetta a errori.

L'oggetto Promise ha due stati: "pending" (in attesa) e "settled" (risolto). Quando una promessa viene creata, è inizialmente nello stato "pending". Quando l'operazione asincrona rappresentata dalla promessa viene completata con successo, la promessa passa allo stato "settled" con lo stato "fulfilled" e restituisce il risultato dell'operazione. Se invece l'operazione asincrona fallisce, la promessa passa allo stato "settled" con lo stato "rejected" e restituisce il motivo del fallimento dell'operazione.

Un oggetto Promise viene creato utilizzando il costruttore Promise, che accetta come argomento una funzione di "risoluzione" (o "resolver" in inglese) che viene eseguita quando l'operazione asincrona rappresentata dalla promessa viene completata. La funzione di risoluzione a sua volta accetta due funzioni di callback come parametri: una per risolvere la promessa (resolve) e l'altra per rifiutare la promessa (reject).

const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      const randomNumber = Math.floor(Math.random() * 10);
      if (randomNumber < 5) {
        resolve(randomNumber);
      } else {
        reject("The random number is greater than or equal to 5.");
      }
    }, 1000);
  });

In questo esempio, viene creata una promessa che rappresenta il completamento di un'operazione che ritorna un numero casuale dopo un secondo. La funzione di risoluzione utilizza il metodo resolve per restituire il valore del numero casuale se il numero è inferiore a 5. Altrimenti, utilizza il metodo reject per rifiutare la promessa e restituire un messaggio di errore. La promessa può poi essere gestita con i metodi then e catch. Il metodo then viene chiamato quando la promessa si risolve con successo, e il metodo catch viene chiamato quando la promessa viene rifiutata. Ad esempio:

promise
    .then(result => console.log(`The random number is ${result}.`))
    .catch(error => console.error(error));

Testa questo esempio sulla console:

function delay(milliseconds) {
    return new Promise(function(resolve) {
      setTimeout(resolve, milliseconds);
    });
  }
  
  function print(text) {
    console.log(text);
  }
  
  delay(1000)
    .then(function() {
      print("Hello, World!");
    })
    .catch(function(error) {
      console.error(error);
    });
  

for loop vs forEach()

In JavaScript, sia forEach() che i cicli for standard possono essere utilizzati per iterare sugli array. Tuttavia, presentano alcune differenze nel loro comportamento e nella sintassi. Ecco alcune delle differenze chiave tra forEach() e i cicli for standard:

  1. forEach() è un metodo nel prototipo dell'array, mentre i cicli for sono una costruzione del linguaggio.
  2. forEach() è tipicamente utilizzato quando si desidera eseguire un'azione su ogni elemento di un array, mentre i cicli for sono spesso utilizzati quando è necessario un maggiore controllo sul processo di iterazione.
  3. forEach() richiede una funzione di callback come argomento, mentre i cicli for utilizzano una variabile di indice per accedere a ciascun elemento dell'array.
  4. forEach() è più veloce quando si itera su tutto un array con operazioni semplici;
const people = [
    { name: "John", age: 30 },
    { name: "Jane", age: 25 },
    { name: "Bob", age: 40 },
    { name: "Alice", age: 35 },
    { name: "David", age: 28 }
  ];
  
  function averageNameLength(people) {
    let sum = 0;
    const startTime = performance.now();
  
//    people.forEach(person => {
//      sum += person.name.length;
//    });
  people.forEach(function(person) {
    sum += person.name.length;
  });

    const endTime = performance.now();
    const timeTaken = (endTime - startTime).toFixed(2);

    console.log(`Time taken using forEach: ${timeTaken}ms`);

    return sum / people.length;
  }
  
  console.log(averageNameLength(people));
  
const people = [
    { name: "John", age: 30 },
    { name: "Jane", age: 25 },
    { name: "Bob", age: 40 },
    { name: "Alice", age: 35 },
    { name: "David", age: 28 }
  ];
  
  function averageNameLength(people) {
    let sum = 0;
    const startTime = performance.now();
  
    for (let i = 0; i < people.length; i++) {
      sum += people[i].name.length;
    }
  
    const endTime = performance.now();

    const timeTaken = (endTime - startTime).toFixed(2);

    console.log(`Time taken using forEach: ${timeTaken}ms`);

    
    return sum / people.length;
  }
  
  console.log(averageNameLength(people));

Esercizi di oggi

Esercizio 1

Partendo dal codice sotto, crea almeno tre persone con le chiavi "name" e "age" + altre tre a piacere.

const people = []

Esercizio 2

Partendo dal codice sotto, scrivi una funzione che calcola l'età media di tutte le persone nell'array

  1. Inizializza una variabile per tenere traccia della somma
  2. Crea un for loop che itera sugli oggetti, e per ogni oggetto aggiungi l'età alla somma
  3. Dividi la somma totale per il numero di persone sull'array
const people = []
  //write a function that calculates the mean age of the people inside the array
averageAge(people)

Esercizio 3

Crea un for loop che itera sull'array e chiama getKey su ogni persona.

const people = []
  //fill the people array 

  //getKey(data) finds the keys of each object and 
  //returns only the first one if it is resolved with resolve(keys[0])
function getKey(data) {
    return new Promise((resolve, reject) => {
      const keys = Object.keys(data);
  if (keys.length > 0) {
    resolve(keys[0]);
  } else {
    reject("The object has no keys.");
  }
    });
  }

//write a for loop that iterates inside the people array 
//and call getKey() on each person
getKey()
.then(key => console.log(`The first key of the object is "${key}".`))
.catch(error => console.error(error));

© Andrea Schimmenti & Fabio Vitali. TW 2022-2023.