JavaScript es esencial para todo desarrollador frontend. A continuación, se exploran conceptos fundamentales que suelen generar confusión y son frecuentemente consultados en entrevistas técnicas.
Tipos Primitivos
JavaScript define seis tipos primitivos:
booleannullundefinednumberstringsymbol
Los primitivos almacenan directamente su valor. No poseen métodos invocables en su forma pura; al intentar usar uno, como undefined.toString(), se produce un error. La expresión '1'.toString() funciona porque el primitivo '1' es convertido internamente a un objeto String.
Un punto común de confusión: aunque typeof null devuelve "object", esto es un defecto histórico del lenguaje. Internamente, null no es un objeto. El tipo number utiliza punto flotante, lo que puede causar resultados inesperados como 0.1 + 0.2 !== 0.3. Las cadenas (string) son inmutables; ningún método puede alterar su valor una vez creado.
Tipos Objeto
Toda estructura que no sea primitiva es un objeto. A diferencia de los primitivos, los objetos almacenan referencias (punteros) en memoria.
const referencia = [];
const otraRef = referencia;
otraRef.push(1);
// 'referencia' y 'otraRef' apuntan al mismo arreglo modificado.
Al pasar un objeto como argumento a una función, se copia su referencia. Dentro de la función, modificar propiedades del parámetro afecta al objeto original. Sin embargo, reasignar el parámetro a un nuevo objeto rompe el vínculo con la referencia original.
function transformar(objeto) {
objeto.edad = 30;
objeto = { nombre: 'Ana', edad: 25 };
return objeto;
}
const original = { nombre: 'Luis', edad: 28 };
const resultado = transformar(original);
console.log(original); // { nombre: 'Luis', edad: 30 }
console.log(resultado); // { nombre: 'Ana', edad: 25 }
Operadores typeof e instanceof
El operador typeof identifica tipos primitivos de manera efectiva, excepto con null. Para objetos, distingue funciones ("function") pero agrupa el resto bajo "object".
instanceof verifica la cadena de prototipos. Funciona para objetos y funciones, pero no directamente con primitivos. Su comportamiento puede personalizarse usando Symbol.hasInstance.
class MiCadena {
static [Symbol.hasInstance](valor) {
return typeof valor === 'string';
}
}
const texto = 'hola';
console.log(texto instanceof MiCadena); // true
Conversión de Tipos
Las conversiones ocurren en tres direcciones: a booleano, a número o a cadena. En contextos booleanos, solo undefined, null, false, NaN, 0, '' y -0 se convierten a false; el resto a true.
Conversión de Objetos a Primitivos
Se sigue un algoritmo: primero se intenta valueOf(), luego toString(). Se puede anular con Symbol.toPrimitive.
const customObj = {
valueOf() { return 10; },
toString() { return 'veinte'; },
[Symbol.toPrimitive](hint) {
return hint === 'number' ? 100 : 'cien';
}
};
console.log(1 + customObj); // 101 (hint: number)
console.log('Valor: ' + customObj); // "Valor: cien"
Operadores Aritméticos
El operador + tiene reglas específicas: si un operando es cadena, el otro se convierte a cadena. En otros casos, se intenta conversión a número.
console.log(true + true); // 2
console.log(4 + [1, 2]); // "41,2"
console.log('a' + + 'b'); // "aNaN"
console.log(4 * '3'); // 12
Comparaciones
En comparaciones (>, <), los objetos se convierten a primitivos mediante valueOf/toString antes de la evaluación.
La Palabra Clave this
El valor de this depende del contexto de invocación:
- Llamada directa (ej.:
funcion()):thises el objeto global (windowen navegadores) en modo no estricto, oundefineden estricto. - Método de objeto (ej.:
obj.metodo()):thises el objeto que posee el método. - Constructor (ej.:
new Funcion()):thises la nueva instancia creada. - Funciones de enlace (
bind,call,apply):thisse establece explícitamente.
function mostrarContexto() {
console.log(this.nombre);
}
const miObj = { nombre: 'Objeto', metodo: mostrarContexto };
mostrarContexto(); // undefined (o global)
miObj.metodo(); // "Objeto"
const nuevaInstancia = new mostrarContexto();
const enlazada = mostrarContexto.bind({ nombre: 'Enlazado' });
enlazada(); // "Enlazado"
Las funciones flecha no tienen su propio this; heredan el del ámbito léxico donde fueron definidas. Los métodos como bind no les afectan.
function crearContador() {
this.valor = 0;
setInterval(() => {
this.valor++; // 'this' es la instancia de crearContador
}, 1000);
}
Prioridad de enlace: new tiene la máxima prioridad, seguido por bind, luego la llamada como método, y finalmente la llamada directa. Las funciones flecha ignoran cualquier intento posterior de reenlazar this.
function tester() { return this.valor; }
const a = { valor: 1 };
const b = { valor: 2 };
const enlazadaA = tester.bind(a);
const enlazadaB = enlazadaA.bind(b);
console.log(enlazadaB()); // 1, no 2. El primer 'bind' prevalece.