Estándares de Codificación JavaScript
Estándares JavaScript
Sección titulada «Estándares JavaScript»XOOPS sigue estándares JavaScript modernos (ES6+) con énfasis en legibilidad y mantenibilidad.
Resumen
Sección titulada «Resumen»Los estándares JavaScript de XOOPS se basan en:
- ECMAScript 2015+ (ES6 y características modernas)
- Guía de Estilo JavaScript de Airbnb (adaptada)
- Convenciones XOOPS para consistencia
- Estándares de accesibilidad (WCAG)
Estructura de Archivos
Sección titulada «Estructura de Archivos»Organización de Archivos
Sección titulada «Organización de Archivos»// 1. Comentario de encabezado del archivo/** * XOOPS Module - Feature Name * @file Handles user interactions on the dashboard * @author Your Name <email@example.com> * @copyright 2026 XOOPS Project * @license GPL-2.0-or-later */
// 2. Importacionesimport { Helper } from './helpers.js';import { API } from './api.js';
// 3. Constantesconst DEFAULT_TIMEOUT = 5000;const API_ENDPOINT = '/api/v1';
// 4. Configuración de móduloconst Dashboard = {};
// 5. Funciones privadasfunction initializeUI() { // ...}
// 6. Métodos públicosDashboard.init = function () { // ...};
// 7. Exportacionesexport default Dashboard;Nomenclatura de Archivos
Sección titulada «Nomenclatura de Archivos»// Usar minúsculas con guionesdashboard.jsuser-profile.jsform-validator.jsapi-client.js
// Componentes React (PascalCase)UserProfile.jsxFormValidator.jsxDashboard.jsxVariables y Constantes
Sección titulada «Variables y Constantes»Declaración de Variables
Sección titulada «Declaración de Variables»// Usar const por defectoconst maxRetries = 3;const userName = 'John';
// Usar let para variables que cambianlet currentIndex = 0;
// Evitar var (legado)// ❌ var oldStyle = true;
// Los objetos const y arrays pueden tener contenidos modificadosconst user = { name: 'John' };user.name = 'Jane'; // ✅ OKuser = {}; // ❌ Error
const numbers = [1, 2, 3];numbers.push(4); // ✅ OKnumbers = []; // ❌ ErrorNombres de Variables
Sección titulada «Nombres de Variables»// Usar nombres descriptivosconst userName = 'John'; // ✅const un = 'John'; // ❌
// Las variables booleanas deben indicar estadoconst isActive = true; // ✅const hasPermission = false; // ✅const canEdit = true; // ✅const active = true; // ❌ Poco claro
// Los arrays deben usar nombres pluralesconst users = ['John', 'Jane'];const userList = ['John', 'Jane'];const items = [];Constantes
Sección titulada «Constantes»// UPPER_SNAKE_CASE para constantes a nivel de móduloconst API_TIMEOUT = 5000;const MAX_RETRIES = 3;const DEFAULT_PAGE_SIZE = 10;
// camelCase para propiedades de objeto (incluso constantes)const config = { apiTimeout: 5000, maxRetries: 3, defaultPageSize: 10,};Funciones
Sección titulada «Funciones»Declaración de Funciones
Sección titulada «Declaración de Funciones»// Funciones nombradas (preferidas para reutilización)function validateEmail(email) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);}
// Funciones flecha (preferidas para callbacks)const validateEmail = (email) => { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);};
// Funciones flecha cortasconst isPositive = (num) => num > 0;const double = (x) => x * 2;
// Evitar expresiones de función anónimas// ❌ const fn = function() {};Nombres de Funciones
Sección titulada «Nombres de Funciones»// Usar nombres descriptivos basados en verbosfunction getUserById(id) { } // ✅ Describe qué obtienefunction validateUserInput(data) { } // ✅ Describe la acciónfunction formatDate(date) { } // ✅ Describe la transformación
// Evitar letras individuales excepto en casos obvios (bucles)function f(x) { } // ❌function fetch() { } // ❌ Conflicto con globalParámetros de Función
Sección titulada «Parámetros de Función»// Usar nombres de parámetros clarosfunction addUser(name, email, role = 'user') { // ...}
// Usar desestructuración para objetosfunction createPost({ title, content, author, published = false }) { // ...}
// Documentar funciones complejas/** * Fetch user data from the API * @param {number} userId - The user ID to fetch * @param {Object} options - Optional settings * @param {boolean} options.includeProfile - Include profile data * @returns {Promise<Object>} User data object */async function fetchUser(userId, options = {}) { const { includeProfile = false } = options; // ...}Clases y Objetos
Sección titulada «Clases y Objetos»Definición de Clases
Sección titulada «Definición de Clases»/** * Represents a user in the system */class User { constructor(name, email) { this.name = name; this.email = email; this.id = null; }
/** * Get user's display name * @returns {string} */ getDisplayName() { return this.name.trim(); }
/** * Validate user email * @returns {boolean} */ isValidEmail() { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email); }}
// Usoconst user = new User('John Doe', 'john@example.com');console.log(user.getDisplayName());Literales de Objeto
Sección titulada «Literales de Objeto»// Usar forma abreviada de objetoconst name = 'John';const age = 30;
// Propiedades abreviadas (ES6)const person = { name, age, getInfo() { return `${this.name} is ${this.age} years old`; },};
// Sin forma abreviada (evitar)// const person = {// name: name,// age: age,// getInfo: function() { }// };Formato
Sección titulada «Formato»Espaciado e Indentación
Sección titulada «Espaciado e Indentación»// Usar 2 espacios para indentación (o 4, ser consistente)function example() { if (true) { console.log('Indented'); }}
// Espacios alrededor de operadoresconst x = 5 + 3; // ✅const y = 5+3; // ❌const z = isDone ? 'yes' : 'no'; // ✅
// Sin espacio dentro de paréntesisif (condition) { } // ✅if ( condition ) { } // ❌
// Espacio antes de llaves de funciónfunction test() { } // ✅function test(){ } // ❌Longitud de Línea
Sección titulada «Longitud de Línea»// Máximo 100 caracteres por línea (o 120)// Romper líneas largas lógicamente
// Cadenas largasconst message = 'This is a very long message that ' + 'continues on the next line';
// Llamadas de función largasconst result = myFunction( parameter1, parameter2, parameter3);
// Condicionales largosif (condition1 && condition2 && condition3) { // ...}Punto y Coma
Sección titulada «Punto y Coma»// Usar punto y comaconst x = 5; // ✅const y = 10;
// No usar punto y coma (ASI - Automatic Semicolon Insertion)const x = 5 // ❌ Evitar confiar en estoCadenas
Sección titulada «Cadenas»Literales de Cadena
Sección titulada «Literales de Cadena»// Usar comillas simples para consistenciaconst name = 'John'; // ✅
// O comillas dobles - solo ser consistenteconst name = "John";
// Usar comillas invertidas para plantillas literales (interpolación)const greeting = `Hello, ${name}!`; // ✅
// Evitar concatenaciónconst message = 'Hello ' + name; // ❌const message = `Hello ${name}`; // ✅
// Cadenas multilíneaconst html = ` <div> <h1>${title}</h1> <p>${content}</p> </div>`;Métodos de Array
Sección titulada «Métodos de Array»// Preferir métodos de array modernosconst 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); // ✅Desestructuración de Array
Sección titulada «Desestructuración de Array»// Extraer elementos de arrayconst [first, second, ...rest] = [1, 2, 3, 4, 5];// first = 1, second = 2, rest = [3, 4, 5]
// Omitir elementosconst [,, third] = [1, 2, 3];// third = 3
// Usar en parámetros de funciónfunction processItems([first, second]) { console.log(first, second);}Objetos
Sección titulada «Objetos»Desestructuración de Objeto
Sección titulada «Desestructuración de Objeto»// Extraer propiedades de objetoconst user = { name: 'John', email: 'john@example.com' };const { name, email } = user;
// Renombrar propiedadesconst { name: userName, email: userEmail } = user;
// Valores por defectoconst { role = 'user' } = user;
// Desestructuración anidadaconst { user: { name, email } } = response;
// Parámetros de funciónfunction displayUser({ name, email, role = 'user' }) { console.log(`${name} (${role})`);}Operador Spread
Sección titulada «Operador Spread»// Copiar arraysconst original = [1, 2, 3];const copy = [...original];
// Fusionar arraysconst merged = [...arr1, ...arr2];
// Copiar objetosconst user = { name: 'John', email: 'john@example.com' };const userCopy = { ...user };
// Fusionar objetosconst merged = { ...defaults, ...options };
// Actualizar propiedadesconst updated = { ...user, email: 'newemail@example.com' };Programación Asincrónica
Sección titulada «Programación Asincrónica»Promises
Sección titulada «Promises»// Promise básicoconst promise = new Promise((resolve, reject) => { if (success) { resolve(result); } else { reject(error); }});
// Métodos de 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
Sección titulada «Async/Await»// Preferido para legibilidadasync 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; }}
// Usoconst user = await fetchUser(123);
// Operaciones múltiplesasync function loadDashboard() { const users = await fetchUsers(); const posts = await fetchPosts(); const comments = await fetchComments();
return { users, posts, comments };}Comentarios y Documentación
Sección titulada «Comentarios y Documentación»Comentarios Inline
Sección titulada «Comentarios Inline»// Explicar POR QUÉ, no QUÉconst result = calculateTotal(items, taxRate); // ✅ Por qué
// ❌ No explicar código obvioconst x = 5; // Set x to 5const sum = a + b; // Add a and bComentarios JSDoc
Sección titulada «Comentarios JSDoc»/** * Calculate the total price of items including tax * * @param {Array<Object>} items - Array of items with price property * @param {number} taxRate - Tax rate as decimal (0.1 = 10%) * @returns {number} Total price including tax * @throws {Error} If items is not an 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);}Manejo de Errores
Sección titulada «Manejo de Errores»Try/Catch
Sección titulada «Try/Catch»// Siempre manejar errorestry { const result = riskyOperation();} catch (error) { console.error('Operation failed:', error);} finally { cleanup();}
// Ser específico con errorestry { const data = JSON.parse(jsonString);} catch (error) { if (error instanceof SyntaxError) { console.error('Invalid JSON'); } else { console.error('Unknown error'); }}Errores Personalizados
Sección titulada «Errores Personalizados»class ValidationError extends Error { constructor(message) { super(message); this.name = 'ValidationError'; }}
// Usoif (!isValidEmail(email)) { throw new ValidationError(`Invalid email: ${email}`);}Manipulación del DOM
Sección titulada «Manipulación del DOM»Seleccionar Elementos
Sección titulada «Seleccionar Elementos»// Métodos modernos (preferidos)const element = document.querySelector('#my-id');const elements = document.querySelectorAll('.my-class');
// Evitar métodos antiguos// const el = document.getElementById('my-id'); // ❌// const els = document.getElementsByClassName('my-class'); // ❌
// Cachear elementosconst button = document.querySelector('button');button.addEventListener('click', handler);Manejo de Eventos
Sección titulada «Manejo de Eventos»// Usar addEventListenerelement.addEventListener('click', (event) => { event.preventDefault(); handleClick();});
// Remover listenerselement.removeEventListener('click', handler);
// Delegación de eventoscontainer.addEventListener('click', (event) => { if (event.target.matches('.item')) { handleItemClick(event.target); }});Actualizaciones del DOM
Sección titulada «Actualizaciones del DOM»// Usar textContent (más seguro que innerHTML)element.textContent = 'Safe text'; // ✅
// Usar innerHTML solo para contenido confiableelement.innerHTML = `<b>${escapeHtml(text)}</b>`;
// Manipulación de claseselement.classList.add('active');element.classList.remove('inactive');element.classList.toggle('disabled');
// Manipulación de atributoselement.setAttribute('data-id', userId);const id = element.getAttribute('data-id');element.removeAttribute('disabled');Patrón de Módulo
Sección titulada «Patrón de Módulo»Módulos ES6
Sección titulada «Módulos ES6»// Exportarexport const helper = () => { };export default Dashboard;
// Importarimport Dashboard from './dashboard.js';import { helper } from './helper.js';import * as utils from './utils.js';Resumen de Mejores Prácticas
Sección titulada «Resumen de Mejores Prácticas»- Usar const por defecto
- Usar nombres descriptivos
- Usar funciones flecha para callbacks
- Usar async/await para promises
- Documentar funciones complejas
- Cachear elementos del DOM
- Usar delegación de eventos
- Escribir funciones puras
- Mantener funciones enfocadas
- No usar var (legado)
- No usar variables globales
- No crear funciones largas (más de 50 líneas)
- No anidar código profundamente
- No usar eval()
- No usar manejadores de eventos en línea
- No dejar console.log() en producción
- No crear fugas de memoria
- No mutar parámetros de función
Herramientas y Linting
Sección titulada «Herramientas y Linting»Configuración ESLint
Sección titulada «Configuración 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" }}Configuración Prettier
Sección titulada «Configuración Prettier»{ "semi": true, "singleQuote": true, "trailingComma": "es5", "printWidth": 100, "tabWidth": 2}Documentación Relacionada
Sección titulada «Documentación Relacionada»- Directrices CSS
- Código de Conducta
- Flujo de Contribución
- Estándares PHP
#xoops #javascript #es6 #coding-standards #best-practices