Domanda: Spiega la differenza tra frontend e backend nello sviluppo web e fai un esempio di tecnologie utilizzate per ciascuno.
Risposta: Il frontend è la parte dell'applicazione web che l'utente vede e con cui interagisce (interfaccia utente), sviluppato con HTML, CSS e JavaScript. Il backend è la parte server che gestisce logica, database e sicurezza, sviluppato con linguaggi come PHP, Python, Node.js. Esempio: un e-commerce dove il frontend mostra i prodotti e il backend gestisce ordini e pagamenti.
Domanda: Qual è la differenza tra tag semantici e tag generici? Fai esempi di entrambi.
Risposta: I tag semantici hanno un significato specifico e descrivono il contenuto (<header>, <nav>, <article>, <footer>), migliorando SEO e accessibilità. I tag generici come <div> e <span> non hanno significato semantico e servono solo per strutturare o stilizzare il contenuto.
Domanda: Cosa significa "mobile-first" nel CSS responsive e perché è considerato una best practice?
Risposta: Mobile-first significa progettare prima per schermi piccoli, poi aggiungere stili per schermi più grandi usando media query con min-width. È una best practice perché la maggioranza del traffico web è mobile, migliora le performance (si caricano meno stili inizialmente) e garantisce un'esperienza utente ottimale su tutti i dispositivi.
Domanda: Spiega il sistema di griglia di Bootstrap e come creare un layout a 3 colonne responsive.
Risposta: Bootstrap usa un sistema a 12 colonne con container, row e col. Per 3 colonne responsive:
<div class="container">
<div class="row">
<div class="col-md-4">Colonna 1</div>
<div class="col-md-4">Colonna 2</div>
<div class="col-md-4">Colonna 3</div>
</div>
</div>Su desktop saranno 3 colonne, su mobile si impilano verticalmente.
Domanda: Qual è la differenza tra let, const e var nella dichiarazione delle variabili?
Risposta: var ha function scope e può essere ridichiarata; let ha block scope e può essere riassegnata ma non ridichiarata; const ha block scope e non può essere né riassegnata né ridichiarata (per oggetti e array il contenuto può essere modificato ma non il riferimento).
Domanda: Come selezionare un elemento HTML tramite JavaScript e aggiungere un event listener?
Risposta:
// Selezione per ID
const button = document.getElementById('mio-button');
// Aggiunta event listener
button.addEventListener('click', function() {
console.log('Button cliccato!');
});Domanda: Cosa significa "progressive enhancement" nello sviluppo web e perché è importante?
Risposta: Progressive enhancement significa partire da una base HTML funzionante per tutti, poi aggiungere CSS per il design e JavaScript per l'interattività. È importante perché garantisce accessibilità, funzionalità anche con JavaScript disabilitato, migliori performance e compatibilità con tutti i browser e dispositivi.
Domanda: Spiega l'importanza dell'attributo alt nelle immagini e quando utilizzare il tag <figure>.
Risposta: L'attributo alt fornisce una descrizione testuale dell'immagine per screen reader e quando l'immagine non si carica, migliorando accessibilità e SEO. Il tag <figure> si usa per contenuti auto-contenuti (immagini, grafici, code) spesso con <figcaption> per la didascalia.
Domanda: Spiega il concetto di specificità in CSS e come viene calcolata.
Risposta: La specificità determina quale regola CSS viene applicata quando ci sono conflitti. Si calcola: inline styles (1000), IDs (100), classi/attributi/pseudo-classi (10), elementi/pseudo-elementi (1). Esempio: #header .nav a ha specificità 111 (1 ID + 1 classe + 1 elemento).
Domanda: Qual è la differenza tra classi utility e componenti in Bootstrap? Fai esempi.
Risposta: Le classi utility sono singole proprietà CSS riutilizzabili (text-center, mb-3, d-flex). I componenti sono elementi UI completi predefiniti (navbar, card, modal). Le utility offrono flessibilità granulare, i componenti forniscono soluzioni complete e coerenti.
Domanda: Spiega la differenza tra == e === con esempi pratici.
Risposta: == confronta i valori con conversione di tipo automatica, === confronta valore e tipo senza conversioni. Esempi:
5 == "5" // true (conversione automatica)
5 === "5" // false (tipi diversi)
0 == false // true
0 === false // falseDomanda: Come utilizzare Flexbox per centrare un elemento sia orizzontalmente che verticalmente?
Risposta:
.container {
display: flex;
justify-content: center; /* centra orizzontalmente */
align-items: center; /* centra verticalmente */
height: 100vh; /* altezza necessaria */
}Domanda: Cosa sono le API REST e perché sono importanti nello sviluppo web moderno?
Risposta: Le API REST sono interfacce che permettono comunicazione tra applicazioni usando HTTP con metodi standard (GET, POST, PUT, DELETE). Sono importanti perché separano frontend e backend, permettono riutilizzo dei servizi, facilitano integrazione tra sistemi diversi e supportano architetture scalabili.
Domanda: Qual è la struttura base di un documento HTML5 e cosa fa il DOCTYPE?
Risposta:
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Titolo</title>
</head>
<body>
<!-- contenuto -->
</body>
</html>Il DOCTYPE dice al browser di usare HTML5 e la modalità standard.
Domanda: Spiega la differenza tra display: none, visibility: hidden e opacity: 0.
Risposta: display: none rimuove completamente l'elemento dal layout; visibility: hidden nasconde l'elemento ma occupa ancora spazio; opacity: 0 rende l'elemento trasparente ma rimane interattivo e occupa spazio. Solo display: none non influenza il layout.
Domanda: Quando è appropriato usare Bootstrap in un progetto e quando invece è meglio evitarlo?
Risposta: Bootstrap è utile per: prototipazione rapida, progetti con budget/tempo limitato, team con competenze CSS variabili, consistenza nel design. Da evitare quando: serve design molto personalizzato, performance critiche, progetti semplici, quando si vuole pieno controllo del CSS.
Domanda: Cosa sono le arrow functions e in cosa differiscono dalle funzioni tradizionali?
Risposta: Le arrow functions sono una sintassi più concisa: const sum = (a, b) => a + b. Differenze principali: non hanno proprio this (lo ereditano dal contesto), non possono essere usate come costruttori, non hanno arguments object, sintassi più breve per funzioni semplici.
Domanda: Cosa significa che JavaScript è single-threaded e come gestisce le operazioni asincrone?
Risposta: JavaScript esegue un'operazione alla volta nel main thread. Le operazioni asincrone (fetch, setTimeout) vengono delegate al browser/Node.js che le gestisce in background. Quando completate, i callback vengono messi nella queue e eseguiti dall'event loop quando il main thread è libero.
Domanda: Qual è la differenza principale tra un ciclo for tradizionale e il metodo forEach? In quali situazioni preferiresti uno sull'altro?
Risposta: Il ciclo for ha controllo completo (break, continue, return), mentre forEach è più dichiarativo ma non può essere interrotto. Usa for quando hai bisogno di uscire anticipatamente o modificare l'indice, forEach per iterazioni semplici senza interruzioni.
// for - può uscire
for(let i = 0; i < array.length; i++) {
if(array[i] === target) break; // OK
}
// forEach - non può uscire
array.forEach(item => {
// break; NON funziona
});Domanda: Analizza questo codice e spiega cosa restituisce map(). Cosa succede se non uso return in alcuni casi?
const numbers = [1, 2, 3, 4, 5];
const result = numbers.map(num => {
if(num % 2 === 0) {
return num * 2;
}
// Cosa succede qui per i numeri dispari?
});
console.log(result);Risposta: map() restituisce sempre un nuovo array della stessa lunghezza dell'originale. Se non c'è return, l'elemento diventa undefined. Il risultato sarà [undefined, 4, undefined, 8, undefined]. Per i numeri dispari manca il return, quindi diventano undefined.
Domanda: Scrivi il codice per filtrare da un array di studenti solo quelli con voto >= 6, poi spiega cosa restituisce filter() se nessun elemento soddisfa la condizione.
Risposta:
const studenti = [
{nome: "Marco", voto: 7},
{nome: "Anna", voto: 5},
{nome: "Luigi", voto: 8}
];
const promossi = studenti.filter(studente => studente.voto >= 6);
// Risultato: [{nome: "Marco", voto: 7}, {nome: "Luigi", voto: 8}]Se nessun elemento soddisfa la condizione, filter() restituisce un array vuoto [], mai null o undefined.
Domanda: Completa questa tabella spiegando le differenze:
| Metodo | Restituisce | Modifica array originale | Uso principale |
|---|---|---|---|
| forEach | ? | ? | ? |
| map | ? | ? | ? |
| filter | ? | ? | ? |
Risposta:
| Metodo | Restituisce | Modifica array originale | Uso principale |
|---|---|---|---|
| forEach | undefined |
No (ma può modificare oggetti contenuti) | Eseguire azioni su ogni elemento |
| map | Nuovo array (stessa lunghezza) | No | Trasformare ogni elemento |
| filter | Nuovo array (lunghezza <= originale) | No | Selezionare elementi che soddisfano condizione |
Domanda: Dato un array di numeri, scrivi il codice per: filtrare solo i pari, moltiplicarli per 3, e sommarli. Spiega l'ordine delle operazioni.
Risposta:
const numbers = [1, 2, 3, 4, 5, 6, 8, 10];
const risultato = numbers
.filter(num => num % 2 === 0) // [2, 4, 6, 8, 10]
.map(num => num * 3) // [6, 12, 18, 24, 30]
.reduce((sum, num) => sum + num, 0); // 90
console.log(risultato); // 90L'ordine è importante: prima filtra (reduce l'array), poi trasforma (map), infine aggrega (reduce).
Domanda: Hai un array di 10.000 elementi e devi trovare il primo elemento che soddisfa una condizione. Quale approccio sceglieresti tra for, forEach, find() e perché?
Risposta: Userei find() o un ciclo for con break.
// MIGLIORE - si ferma al primo match
const result = array.find(item => item.condition);
// O ciclo for con break
for(let i = 0; i < array.length; i++) {
if(array[i].condition) {
result = array[i];
break; // IMPORTANTE: esce subito
}
}
// PESSIMO - controlla tutto l'array anche dopo aver trovato
array.forEach(item => { /* non può fermarsi */ });forEach continuerebbe a controllare tutti i 10.000 elementi anche dopo aver trovato il primo.
Domanda: Analizza questi due approcci per incrementare tutti i numeri in un array. Quale è preferibile e perché?
// Approccio A
const numbers = [1, 2, 3];
for(let i = 0; i < numbers.length; i++) {
numbers[i]++;
}
// Approccio B
const numbers = [1, 2, 3];
const incremented = numbers.map(num => num + 1);Risposta: L'Approccio B è preferibile perché:
- Immutabilità: non modifica l'array originale
- Predicibilità:
numbersrimane invariato,incrementedè il nuovo array - Functional programming: più facile da testare e debuggare
- Evita side effects: non ci sono modifiche inaspettate ad altri punti del codice che usano
numbers
// Approccio A: numbers diventa [2, 3, 4] (MODIFICATO)
// Approccio B: numbers rimane [1, 2, 3], incremented è [2, 3, 4]Domanda: Posso con solo uso di map() fare in modo di avere un nuovo array che contenga solo i valori desiderati?
Risposta:
Con map() non puoi davvero "non restituire niente" perché mantiene sempre la stessa lunghezza dell'array originale. Ecco pero' le opzioni:
const numbers = [1, 2, 3, 4, 5];
const result = numbers.map(num => {
if(num % 2 === 0) {
return num * 2;
}
// Non restituisce nulla = undefined
});
console.log(result); // [undefined, 4, undefined, 8, undefined]const numbers = [1, 2, 3, 4, 5];
const result = numbers.flatMap(num => {
if(num % 2 === 0) {
return [num * 2]; // Array con un elemento
}
return []; // Array vuoto = niente nell'output finale
});
console.log(result); // [4, 8] - gli array vuoti spariscono!const numbers = [1, 2, 3, 4, 5];
const result = numbers
.filter(num => num % 2 === 0) // Solo pari: [2, 4]
.map(num => num * 2); // Trasforma: [4, 8]
console.log(result); // [4, 8]const numbers = [1, 2, 3, 4, 5];
const result = numbers.reduce((acc, num) => {
if(num % 2 === 0) {
acc.push(num * 2);
}
// Se non è pari, non aggiungo nulla
return acc;
}, []);
console.log(result); // [4, 8]Domanda: "Hai un array di studenti e vuoi ottenere solo i nomi di quelli promossi (voto >= 6), trasformati in maiuscolo. Confronta questi tre approcci:"
const studenti = [
{nome: "marco", voto: 7},
{nome: "anna", voto: 4},
{nome: "luigi", voto: 8},
{nome: "sara", voto: 5}
];
// Approccio 1: map (SBAGLIATO)
const result1 = studenti.map(s => {
if(s.voto >= 6) return s.nome.toUpperCase();
// undefined per i bocciati
});
// Risultato: ["MARCO", undefined, "LUIGI", undefined]
// Approccio 2: filter + map (CORRETTO)
const result2 = studenti
.filter(s => s.voto >= 6)
.map(s => s.nome.toUpperCase());
// Risultato: ["MARCO", "LUIGI"]
// Approccio 3: flatMap (ALTERNATIVA)
const result3 = studenti.flatMap(s => {
if(s.voto >= 6) return [s.nome.toUpperCase()];
return [];
});
// Risultato: ["MARCO", "LUIGI"]La regola d'oro: Se vuoi un array più piccolo dell'originale, usa filter() poi map(), non solo map()!
Questo è uno dei concetti più importanti e spesso fraintesi di JavaScript. Approfondiamo tutto:
JavaScript ha un solo thread principale (main thread) che esegue il codice. Significa che può fare una sola cosa alla volta:
console.log("1");
console.log("2");
console.log("3");
// Eseguite in sequenza, una dopo l'altraMa allora come fa a gestire operazioni che richiedono tempo (API, timer, file)?
Il browser (o Node.js) fornisce Web APIs che girano in background:
console.log("Inizio"); // 1° - Sincrono
setTimeout(() => {
console.log("Timer finito"); // 4° - Asincrono
}, 2000);
fetch('https://api.github.com/users/octocat')
.then(response => response.json())
.then(data => console.log(data)); // 5° - Asincrono
console.log("Fine"); // 2° - Sincrono
// Output:
// "Inizio"
// "Fine"
// (dopo 2 secondi) "Timer finito"
// (quando arriva risposta) {...dati utente...}CALL STACK → WEB APIs → CALLBACK QUEUE → EVENT LOOP
// Esempio pratico
console.log("1 - Sincrono");
setTimeout(() => {
console.log("3 - Da timer");
}, 0); // Anche con 0ms!
Promise.resolve().then(() => {
console.log("2 - Da Promise");
});
console.log("4 - Sincrono");
// Output: 1, 4, 2, 3
// Le Promise hanno priorità sui timer!Perché questo ordine?
console.logva direttamente nel Call StacksetTimeoutva nelle Web APIs, poi nella Macro QueuePromiseva nella Micro Queue (priorità maggiore)- Event Loop controlla prima Micro Queue, poi Macro Queue
// CALLBACK HELL - Da evitare!
function getUserData(userId, callback) {
// Simula chiamata API
setTimeout(() => {
const user = { id: userId, name: "Marco" };
callback(null, user);
}, 1000);
}
function getUserPosts(userId, callback) {
setTimeout(() => {
const posts = ["Post 1", "Post 2"];
callback(null, posts);
}, 1000);
}
// Uso - diventa un disastro!
getUserData(1, (err, user) => {
if(err) return console.error(err);
getUserPosts(user.id, (err, posts) => {
if(err) return console.error(err);
console.log(user, posts);
// E se dovessi fare un'altra chiamata?
// Altri callback annidati...
});
});// Stessa logica con Promise
function getUserData(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if(userId <= 0) {
reject(new Error("ID non valido"));
} else {
resolve({ id: userId, name: "Marco" });
}
}, 1000);
});
}
function getUserPosts(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(["Post 1", "Post 2"]);
}, 1000);
});
}
// Uso con .then() - Molto più pulito!
getUserData(1)
.then(user => {
console.log("User:", user);
return getUserPosts(user.id);
})
.then(posts => {
console.log("Posts:", posts);
})
.catch(error => {
console.error("Errore:", error);
});// GET Request
fetch('https://jsonplaceholder.typicode.com/users/1')
.then(response => {
if(!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(user => {
console.log(user);
document.getElementById('user-name').textContent = user.name;
})
.catch(error => {
console.error('Errore fetch:', error);
});
// POST Request
const newUser = {
name: 'Marco Rossi',
email: '[email protected]'
};
fetch('https://jsonplaceholder.typicode.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(newUser)
})
.then(response => response.json())
.then(data => console.log('Utente creato:', data))
.catch(error => console.error('Errore:', error));// Stesso codice ma più leggibile
async function fetchUserAndPosts(userId) {
try {
// Le operazioni sembrano sincrone ma sono asincrone!
const user = await getUserData(userId);
console.log("User:", user);
const posts = await getUserPosts(user.id);
console.log("Posts:", posts);
return { user, posts };
} catch (error) {
console.error("Errore:", error);
throw error; // Re-throw per chi chiama la funzione
}
}
// Uso
fetchUserAndPosts(1)
.then(result => {
console.log("Risultato completo:", result);
});
// O dentro un'altra async function
async function main() {
const result = await fetchUserAndPosts(1);
console.log("Risultato:", result);
}// SEQUENZIALE - Una dopo l'altra (più lento)
async function sequential() {
console.time("Sequential");
const user1 = await fetch('/api/users/1').then(r => r.json());
const user2 = await fetch('/api/users/2').then(r => r.json());
const user3 = await fetch('/api/users/3').then(r => r.json());
console.timeEnd("Sequential"); // ~3 secondi
return [user1, user2, user3];
}
// PARALLELO - Tutte insieme (più veloce)
async function parallel() {
console.time("Parallel");
const [user1, user2, user3] = await Promise.all([
fetch('/api/users/1').then(r => r.json()),
fetch('/api/users/2').then(r => r.json()),
fetch('/api/users/3').then(r => r.json())
]);
console.timeEnd("Parallel"); // ~1 secondo
return [user1, user2, user3];
}async function robustApiCall() {
const maxRetries = 3;
let attempt = 0;
while(attempt < maxRetries) {
try {
const response = await fetch('/api/data');
if(!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
attempt++;
console.log(`Tentativo ${attempt} fallito:`, error.message);
if(attempt >= maxRetries) {
throw new Error(`Falliti tutti i ${maxRetries} tentativi`);
}
// Aspetta prima di riprovare (exponential backoff)
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
}Domanda A: Cosa stampa questo codice e in che ordine?
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');Risposta: 1, 4, 3, 2 (Promise ha priorità su setTimeout)
Domanda B: Come faresti questa chiamata in parallelo invece che in sequenza?
// Da migliorare
async function getData() {
const users = await fetch('/api/users').then(r => r.json());
const products = await fetch('/api/products').then(r => r.json());
return { users, products };
}Risposta:
async function getData() {
const [users, products] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/products').then(r => r.json())
]);
return { users, products };
}Il punto chiave: JavaScript è single-threaded ma non bloccante grazie all'Event Loop e alle Web APIs che gestiscono le operazioni asincrone in background!