Standard di Codifica JavaScript
Standard JavaScript
Sezione intitolata “Standard JavaScript”XOOPS segue standard JavaScript moderni (ES6+) con enfasi su leggibilità e manutenibilità.
Panoramica
Sezione intitolata “Panoramica”Gli standard JavaScript di XOOPS sono basati su:
- ECMAScript 2015+ (ES6 e funzionalità moderne)
- Airbnb JavaScript Style Guide (adattata)
- Convenzioni XOOPS per coerenza
- Standard accessibilità (WCAG)
Struttura File
Sezione intitolata “Struttura File”Organizzazione File
Sezione intitolata “Organizzazione File”// 1. Commento header file/** * XOOPS Module - Feature Name * @file Gestisce interazioni utente sul dashboard * @author Your Name <email@example.com> * @copyright 2026 XOOPS Project * @license GPL-2.0-or-later */
// 2. Importimport { Helper } from './helpers.js';import { API } from './api.js';
// 3. Costanticonst DEFAULT_TIMEOUT = 5000;const API_ENDPOINT = '/api/v1';
// 4. Setup moduloconst Dashboard = {};
// 5. Funzioni privatefunction initializeUI() { // ...}
// 6. Metodi pubbliciDashboard.init = function () { // ...};
// 7. Exportexport default Dashboard;Naming File
Sezione intitolata “Naming File”// Usa lowercase con trattinidashboard.jsuser-profile.jsform-validator.jsapi-client.js
// Componenti React (PascalCase)UserProfile.jsxFormValidator.jsxDashboard.jsxVariabili e Costanti
Sezione intitolata “Variabili e Costanti”Dichiarazione Variabili
Sezione intitolata “Dichiarazione Variabili”// Usa const per defaultconst maxRetries = 3;const userName = 'John';
// Usa let per variabili che cambianolet currentIndex = 0;
// Evita var (legacy)// ❌ var oldStyle = true;
// Oggetti e array const possono avere contenuti modificaticonst user = { name: 'John' };user.name = 'Jane'; // ✅ OKuser = {}; // ❌ Errore
const numbers = [1, 2, 3];numbers.push(4); // ✅ OKnumbers = []; // ❌ ErroreNaming Variabili
Sezione intitolata “Naming Variabili”// Usa nomi descrittiviconst userName = 'John'; // ✅const un = 'John'; // ❌
// Le variabili booleane devono indicare lo statoconst isActive = true; // ✅const hasPermission = false; // ✅const canEdit = true; // ✅const active = true; // ❌ Poco chiaro
// Array devono usare nomi pluraliconst users = ['John', 'Jane'];const userList = ['John', 'Jane'];const items = [];Costanti
Sezione intitolata “Costanti”// UPPER_SNAKE_CASE per costanti a livello moduloconst API_TIMEOUT = 5000;const MAX_RETRIES = 3;const DEFAULT_PAGE_SIZE = 10;
// camelCase per proprietà oggetto (anche costanti)const config = { apiTimeout: 5000, maxRetries: 3, defaultPageSize: 10,};Funzioni
Sezione intitolata “Funzioni”Dichiarazione Funzione
Sezione intitolata “Dichiarazione Funzione”// Funzioni nominate (preferite per riusabilità)function validateEmail(email) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);}
// Arrow function (preferite per callback)const validateEmail = (email) => { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);};
// Short arrow functionconst isPositive = (num) => num > 0;const double = (x) => x * 2;
// Evita espressioni funzione anonime// ❌ const fn = function() {};Naming Funzione
Sezione intitolata “Naming Funzione”// Usa nomi descrittivi basati su verbofunction getUserById(id) { } // ✅ Descrive cosa prendefunction validateUserInput(data) { } // ✅ Descrive azionefunction formatDate(date) { } // ✅ Descrive trasformazione
// Evita singole lettere eccetto in casi ovvi (loop)function f(x) { } // ❌function fetch() { } // ❌ Conflitto con globaleParametri Funzione
Sezione intitolata “Parametri Funzione”// Usa nomi parametri chiarifunction addUser(name, email, role = 'user') { // ...}
// Usa destructuring per oggettifunction createPost({ title, content, author, published = false }) { // ...}
// Documenta funzioni complesse/** * Recupera dati utente da API * @param {number} userId - L'ID utente da recuperare * @param {Object} options - Impostazioni opzionali * @param {boolean} options.includeProfile - Includi dati profilo * @returns {Promise<Object>} Oggetto dati utente */async function fetchUser(userId, options = {}) { const { includeProfile = false } = options; // ...}Classi e Oggetti
Sezione intitolata “Classi e Oggetti”Definizione Classe
Sezione intitolata “Definizione Classe”/** * Rappresenta un utente nel sistema */class User { constructor(name, email) { this.name = name; this.email = email; this.id = null; }
/** * Ottiene nome display utente * @returns {string} */ getDisplayName() { return this.name.trim(); }
/** * Convalida email utente * @returns {boolean} */ isValidEmail() { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email); }}
// Utilizzoconst user = new User('John Doe', 'john@example.com');console.log(user.getDisplayName());Object Literals
Sezione intitolata “Object Literals”// Usa shorthand oggettoconst name = 'John';const age = 30;
// Proprietà shorthand (ES6)const person = { name, age, getInfo() { return `${this.name} è ${this.age} anni`; },};
// Senza shorthand (evita)// const person = {// name: name,// age: age,// getInfo: function() { }// };Formattazione
Sezione intitolata “Formattazione”Spacing e Indentazione
Sezione intitolata “Spacing e Indentazione”// Usa 2 spazi per indentazione (o 4, sii coerente)function example() { if (true) { console.log('Indentato'); }}
// Spazi attorno operatoriconst x = 5 + 3; // ✅const y = 5+3; // ❌const z = isDone ? 'yes' : 'no'; // ✅
// Nessuno spazio dentro parentesiif (condition) { } // ✅if ( condition ) { } // ❌
// Spazio prima function bracesfunction test() { } // ✅function test(){ } // ❌Lunghezza Linea
Sezione intitolata “Lunghezza Linea”// Massimo 100 caratteri per linea (o 120)// Spezza lunghe linee logicamente
// Stringhe lungheconst message = 'Questo è un messaggio molto lungo che ' + 'continua sulla linea successiva';
// Lunghe chiamate funzioneconst result = myFunction( parameter1, parameter2, parameter3);
// Lunghi condizionaliif (condition1 && condition2 && condition3) { // ...}Semicoloni
Sezione intitolata “Semicoloni”// Usa semicoloniconst x = 5; // ✅const y = 10;
// Non usare semicoloni (ASI - Automatic Semicolon Insertion)const x = 5 // ❌ Evita di affidarsi a questoStringhe
Sezione intitolata “Stringhe”String Literals
Sezione intitolata “String Literals”// Usa single quote per coerenzaconst name = 'John'; // ✅
// O double quote - sii coerenteconst name = "John";
// Usa backtick per template literal (interpolazione)const greeting = `Hello, ${name}!`; // ✅
// Evita concatenazioneconst message = 'Hello ' + name; // ❌const message = `Hello ${name}`; // ✅
// Stringhe multi-lineaconst html = ` <div> <h1>${title}</h1> <p>${content}</p> </div>`;Metodi Array
Sezione intitolata “Metodi Array”// Preferisci metodi array moderniconst numbers = [1, 2, 3, 4, 5];
// Mapconst doubled = numbers.map(n => n * 2); // ✅// for (let i = 0; i < numbers.length; i++) { } // ❌
// Filterconst evens = numbers.filter(n => n % 2 === 0); // ✅
// Reduceconst sum = numbers.reduce((acc, n) => acc + n, 0); // ✅
// Findconst first = numbers.find(n => n > 3); // ✅
// Some/Everyconst hasEven = numbers.some(n => n % 2 === 0); // ✅const allPositive = numbers.every(n => n > 0); // ✅Array Destructuring
Sezione intitolata “Array Destructuring”// Estrai elementi arrayconst [first, second, ...rest] = [1, 2, 3, 4, 5];// first = 1, second = 2, rest = [3, 4, 5]
// Salta elementiconst [,, third] = [1, 2, 3];// third = 3
// Usa nei parametri funzionefunction processItems([first, second]) { console.log(first, second);}Oggetti
Sezione intitolata “Oggetti”Object Destructuring
Sezione intitolata “Object Destructuring”// Estrai proprietà oggettoconst user = { name: 'John', email: 'john@example.com' };const { name, email } = user;
// Rinomina proprietàconst { name: userName, email: userEmail } = user;
// Valori defaultconst { role = 'user' } = user;
// Destructuring annidatoconst { user: { name, email } } = response;
// Parametri funzionefunction displayUser({ name, email, role = 'user' }) { console.log(`${name} (${role})`);}Spread Operator
Sezione intitolata “Spread Operator”// Copia arrayconst original = [1, 2, 3];const copy = [...original];
// Merge arrayconst merged = [...arr1, ...arr2];
// Copia oggetticonst user = { name: 'John', email: 'john@example.com' };const userCopy = { ...user };
// Merge oggetticonst merged = { ...defaults, ...options };
// Aggiorna proprietàconst updated = { ...user, email: 'newemail@example.com' };Programmazione Asincrona
Sezione intitolata “Programmazione Asincrona”Promise
Sezione intitolata “Promise”// Promise baseconst promise = new Promise((resolve, reject) => { if (success) { resolve(result); } else { reject(error); }});
// Metodi PromisePromise.all([p1, p2, p3]) .then(results => console.log(results)) .catch(error => console.error(error));
Promise.race([p1, p2]) .then(result => console.log(result));Async/Await
Sezione intitolata “Async/Await”// Preferita per leggibilitàasync function fetchUser(userId) { try { const response = await fetch(`/api/users/${userId}`); if (!response.ok) throw new Error('User not found'); const data = await response.json(); return data; } catch (error) { console.error('Failed to fetch user:', error); throw error; }}
// Utilizzoconst user = await fetchUser(123);
// Operazioni multipleasync function loadDashboard() { const users = await fetchUsers(); const posts = await fetchPosts(); const comments = await fetchComments();
return { users, posts, comments };}Commenti e Documentazione
Sezione intitolata “Commenti e Documentazione”Commenti Inline
Sezione intitolata “Commenti Inline”// Spiega PERCHÉ, non COSAconst result = calculateTotal(items, taxRate); // ✅ Perché
// ❌ Non spiegare codice ovvioconst x = 5; // Imposta x a 5const sum = a + b; // Somma a e bCommenti JSDoc
Sezione intitolata “Commenti JSDoc”/** * Calcola il prezzo totale degli articoli incluse tasse * * @param {Array<Object>} items - Array di articoli con proprietà price * @param {number} taxRate - Aliquota tasse come decimale (0.1 = 10%) * @returns {number} Prezzo totale incluse tasse * @throws {Error} Se items non è un array * @example * const total = calculateTotal( * [{ price: 100 }, { price: 50 }], * 0.1 * ); * console.log(total); // 165 */function calculateTotal(items, taxRate = 0) { if (!Array.isArray(items)) { throw new Error('Items must be an array'); }
const subtotal = items.reduce((sum, item) => sum + item.price, 0); return subtotal * (1 + taxRate);}Gestione Errori
Sezione intitolata “Gestione Errori”Try/Catch
Sezione intitolata “Try/Catch”// Gestisci sempre gli erroritry { const result = riskyOperation();} catch (error) { console.error('Operation failed:', error);} finally { cleanup();}
// Sii specifico con gli erroritry { const data = JSON.parse(jsonString);} catch (error) { if (error instanceof SyntaxError) { console.error('Invalid JSON'); } else { console.error('Unknown error'); }}Errori Personalizzati
Sezione intitolata “Errori Personalizzati”class ValidationError extends Error { constructor(message) { super(message); this.name = 'ValidationError'; }}
// Utilizzoif (!isValidEmail(email)) { throw new ValidationError(`Invalid email: ${email}`);}Manipolazione DOM
Sezione intitolata “Manipolazione DOM”Selezione Elementi
Sezione intitolata “Selezione Elementi”// Metodi moderni (preferiti)const element = document.querySelector('#my-id');const elements = document.querySelectorAll('.my-class');
// Evita metodi precedenti// const el = document.getElementById('my-id'); // ❌// const els = document.getElementsByClassName('my-class'); // ❌
// Cache elementiconst button = document.querySelector('button');button.addEventListener('click', handler);Event Handling
Sezione intitolata “Event Handling”// Usa addEventListenerelement.addEventListener('click', (event) => { event.preventDefault(); handleClick();});
// Rimuovi listenerelement.removeEventListener('click', handler);
// Event delegationcontainer.addEventListener('click', (event) => { if (event.target.matches('.item')) { handleItemClick(event.target); }});Aggiornamenti DOM
Sezione intitolata “Aggiornamenti DOM”// Usa textContent (più sicuro di innerHTML)element.textContent = 'Safe text'; // ✅
// Usa innerHTML solo per contenuto trustedelement.innerHTML = `<b>${escapeHtml(text)}</b>`;
// Manipolazione classielement.classList.add('active');element.classList.remove('inactive');element.classList.toggle('disabled');
// Manipolazione attributielement.setAttribute('data-id', userId);const id = element.getAttribute('data-id');element.removeAttribute('disabled');Modello Modulo
Sezione intitolata “Modello Modulo”Moduli ES6
Sezione intitolata “Moduli ES6”// Exportexport const helper = () => { };export default Dashboard;
// Importimport Dashboard from './dashboard.js';import { helper } from './helper.js';import * as utils from './utils.js';Riassunto Best Practice
Sezione intitolata “Riassunto Best Practice”Da Fare
Sezione intitolata “Da Fare”- Usa const per default
- Usa nomi descrittivi
- Usa arrow function per callback
- Usa async/await per promise
- Documenta funzioni complesse
- Cache elementi DOM
- Usa event delegation
- Scrivi funzioni pure
- Mantieni funzioni focalizzate
Da Non Fare
Sezione intitolata “Da Non Fare”- Non usare var (legacy)
- Non usare variabili globali
- Non creare funzioni lunghe (oltre 50 linee)
- Non annidare codice profondamente
- Non usare eval()
- Non usare event handler inline
- Non lasciare console.log() in produzione
- Non creare memory leak
- Non mutare parametri funzione
Strumenti e Linting
Sezione intitolata “Strumenti e Linting”Configurazione ESLint
Sezione intitolata “Configurazione ESLint”{ "env": { "browser": true, "es2021": true, "node": true }, "extends": ["eslint:recommended"], "rules": { "indent": ["error", 2], "quotes": ["error", "single"], "semi": ["error", "always"], "no-var": "error", "prefer-const": "error" }}Configurazione Prettier
Sezione intitolata “Configurazione Prettier”{ "semi": true, "singleQuote": true, "trailingComma": "es5", "printWidth": 100, "tabWidth": 2}Documentazione Correlata
Sezione intitolata “Documentazione Correlata”- Linee Guida CSS
- Codice di Condotta
- Workflow Contribuzione
- Standard PHP
#xoops #javascript #es6 #coding-standards #best-practices