Guía Práctica de JavaScript: Combinaciones, Asincronía y Patrones de Ejecución

  1. Generación de Combinaciones de Elementos de un Arreglo

Para calcular todas las combinaciones posibles de los elementos de un array, podemos aprovechar las operaciones a nivel de bit. Cada número entero puede representar una máscara de bits donde cada bit indica si un elemento específico está incluido en la combinación actual.

const elementos = ['x', 'y', 'z', 'w'];
const totalCombinaciones = Math.pow(2, elementos.length);
const resultados = Array.from({ length: elementos.length }, () => []);

for (let mascara = 1; mascara < totalCombinaciones; mascara++) {
    const combinacionActual = [];
    for (let indice = 0; indice < elementos.length; indice++) {
        // Verificamos si el bit en la posición 'indice' está activo
        if ((mascara & (1 << indice)) !== 0) {
            combinacionActual.push(elementos[indice]);
        }
    }
    // Agrupamos las combinaciones por su longitud
    resultados[combinacionActual.length - 1].push(combinacionActual.join(''));
}

console.log(resultados);

  1. El Contexto de Ejecución y la Palabra Clave this

El valor de this en JavaScript no se define en tiempo de escritura, sino en tiempo de ejecución, dependiendo de cómo se invoca la función. Las funciones de flecha (arrow functions) introducidas en ES6 no tienen su propio this, sino que heredan el del contexto léxico superior.

const boton = document.querySelector('#miBoton');
boton.addEventListener('click', function() {
    console.log(this); // Hace referencia al elemento DOM 'boton'
    
    const funcionInterna = () => {
        console.log(this); // Hereda el 'this' del ámbito padre (el elemento 'boton')
    };
    funcionInterna();
});

const objetoConfig = {
    nombre: 'Sistema',
    iniciar: function() {
        console.log(this); // Hace referencia a 'objetoConfig'
        
        function procesoInterno() {
            console.log(this); // En modo no estricto, hace referencia al objeto global (window)
        }
        procesoInterno();
    }
};
objetoConfig.iniciar();

  1. Manejo de Temporizadores y el Bucle de Eventos

JavaScript opera en un único hilo principal. Las funciones como setTimeout y setInterval no garantizan una ejecución exacta tras el tiempo especificado; en su lugar, insertan la función de callback en la cola de tareas (macro-tasks) una vez que el tiempo ha expirado. El callback solo se ejecutará cuando el hilo principal esté libre y el bucle de eventos (Event Loop) lo procese.

Un error común al usar var en bucles con temporizadores es la pérdida del valor de la variable de iteración debido al alcance de función. Esto se resuelve utilizando let o pasando el valor como argumento adicional al temporizador.

// Solución moderna usando let (crea un nuevo scope por iteración)
for (let contador = 0; contador < 5; contador++) {
    setTimeout(() => {
        console.log(`Iteración: ${contador}`);
    }, contador * 100);
}

// Solución clásica pasando el parámetro directamente a setTimeout
for (var indice = 0; indice < 5; indice++) {
    setTimeout(function(valor) {
        console.log(`Valor capturado: ${valor}`);
    }, indice * 100, indice);
}

Por otro lado, setInterval puede acumular ejecuciones en la cola si el hilo principal está bloqueado, provocando que se ejecuten múltiples callbacks sin intervalo entre ellos. Para evitar esto, es preferible utilizar llamadas recursivas con setTimeout.

  1. Eventos en Línea y Extracción de Valores en Formularios

En lugar de utilizar manejadores de eventos en línea dentro del HTML, la práctica recomendada es separar la estructura del comportamiento utilizando addEventListener. Esto facilita el mantenimiento y evita problemas con el contexto de ejecución.

const selectorMenu = document.getElementById('selectorPlatos');

selectorMenu.addEventListener('change', (evento) => {
    const valorSeleccionada = evento.target.value;
    const textoSeleccionado = evento.target.options[evento.target.selectedIndex].text;
    console.log(`ID: ${valorSeleccionada}, Plato: ${textoSeleccionado}`);
});

  1. Evaluación de Cadenas en Contextos Booleanos y Coerción de Tipos

La coerción de tipos en JavaScript puede generar resultados contraintuitivos al evaluar cadenas de texto. La cadena '0' es considerada true en un contexto booleano estricto, ya que cualquier cadena no vacía es verdadera. Sin embargo, al usar el operador de igualdad débil (==), la cadena se convierte a número, resultando en 0 == false, lo cual evalúa a true.

  • Boolean('0') resulta en true.
  • '0' == false resulta en true (ambos se coercionan al número 0).
  • '0' === false resulta en false (no hay coerción, los tipos son distintos).
  1. Detección de la Tecla Enter Compatible con Navegadores

Para capturar la pulsación de la tecla Enter de manera robusta, se debe priorizar la propiedad moderna key y mantener las propiedades heredadas como respaldo para navegadores antiguos.

const campoTexto = document.getElementById('entradaUsuario');

campoTexto.addEventListener('keydown', (evento) => {
    const esTeclaEnter = evento.key === 'Enter' || evento.keyCode === 13 || evento.which === 13;
    
    if (esTeclaEnter) {
        evento.preventDefault();
        console.log('Tecla Enter presionada, procesando formulario...');
    }
});

  1. Expresiones de Función Ejecutadas Inmediatamente (IIFE)

Un IIFE (Immediately Invoked Function Expression) es un patrón de diseño que ejecuta una función tan pronto como se define. Se utiliza pricnipalmente para crear un ámbito léxico privado y evitar la contaminación del espacio de nombres global. El parser de JavaScript requiere que la función sea tratada como una expresión y no como una declaración, lo cual se logra envolviéndola en paréntesis o utilizando operadores unarios.

// IIFE estándar con paso de parámetros
const miModulo = (function(dependenciaExterna) {
    let estadoPrivado = 0;
    
    return {
        incrementar: () => ++estadoPrivado,
        obtenerEstado: () => estadoPrivado + dependenciaExterna
    };
})(10);

console.log(miModulo.obtenerEstado()); // 10

Este patrón es la base del formato UMD (Universal Module Definition), que permite que el código funcione tanto en entornos de módulos AMD (como RequireJS), CommonJS (Node.js) o como variables globales en el navegador.

(function(root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['dependencia'], factory);
    } else if (typeof module === 'object' && module.exports) {
        module.exports = factory(require('dependencia'));
    } else {
        root.miLibreria = factory(root.dependencia);
    }
}(typeof self !== 'undefined' ? self : this, function(dependencia) {
    return {
        version: '2.0.0',
        iniciar: () => console.log('Módulo iniciado')
    };
}));

  1. Iteración sobre Arreglos y Objetos

JavaScript ofrece múltiples mecanismos para recorrer estructuras de datos. La elección del método depende de si se necesita romper el bucle, transformar los datos o iterar sobre propiedades de objetos.

  • Bucle for clásico: Óptimo para rendimiento y permite el uso de break y continue.
  • for...of: Introducido en ES6, ideal para iterar sobre valores de objetos iterables (Arrays, Maps, Sets).
  • forEach: Método de arreglos que ejecuta una función por cada elemento. No permite interrumpir la iteración con break.
  • map: Similar a forEach, pero retorna un nuevo arreglo con los resultados de la función de callback.
  • for...in: Diseñado para iterar sobre las propiedades enumerables de un objeto. No se recomienda para arreglos debido a que puede iterar sobre propiedades heredadas y el orden no está garantizado.
  • Object.keys(), Object.values(), Object.entries(): Métodos estáticos modernos para extraer y recorrer las propiedades de los objetos de forma segura.
  1. Generación de Plantillas HTML

Históricamente, plugins como jQuery.tmpl se utilizaban para renderizar plantillas pasando objetos de datos. En el desarrollo moderno, estos plugins han quedado obsoletos en favor de las plantillas literales (Template Literals) de ES6, que permiten la interpolación de variables y la ejecución de expresiones directamente dentro de cadenas de texto multilínea.

const datos = { 
    titulo: 'Menú del Día', 
    platos: [
        { id: 1, nombre: 'Ensalada César', precio: 12.50 },
        { id: 2, nombre: 'Pasta Carbonara', precio: 15.00 }
    ] 
};

const html = `
    <div class="menu-contenedor">
        <h2>${datos.titulo}</h2>
        <ul>
            ${datos.platos.map(plato => `
                <li data-id="${plato.id}">
                    ${plato.nombre} - <strong>$${plato.precio.toFixed(2)}</strong>
                </li>
            `).join('')}
        </ul>
    </div>
`;

document.getElementById('contenedorPrincipal').innerHTML = html;

Etiquetas: JavaScript ecmascript IIFE Bucle de Eventos DOM

Publicado el 6-23 22:33