Vienes de un lenguaje donde cada variable tiene un tipo fijo y el compilador vigila que no lo mezcles. JavaScript funciona al revés: los tipos existen, pero van con los valores, y cuando mezclas tipos el lenguaje no protesta, sino que intenta convertirlos. Conocer esas reglas es la diferencia entre entender un resultado raro y quedarte mirándolo.
1 // Declarar variables: let y const
JavaScript tiene tres palabras para declarar variables, pero en código moderno sólo se usan dos:
const iva = 0.21; // no se puede reasignar
let total = 100; // se puede reasignar
total = total * (1 + iva);
var antiguo = 'evítalo'; // la forma de antes de 2015La regla práctica es sencilla: usa const por defecto y cambia a let sólo cuando necesites reasignar. Así, cuando alguien lee let, sabe que ese valor va a cambiar más adelante. var tiene un comportamiento de ámbito distinto, que verás en la unidad de ámbito y closures, y no aporta nada que let no haga mejor.
const no significa inmutable. Impide reasignar la variable, pero no impide modificar el valor si este es un objeto o un array. Lo verás con calma en la sección 3, cuando conozcas los tipos.
2 // Los tipos de JavaScript
No declaras el tipo, pero cada valor tiene uno. Hay siete tipos primitivos y un tipo objeto, que engloba todo lo demás (arrays, funciones, fechas...):
| Tipo | Valores | typeof |
|---|---|---|
number |
42, 3.14, NaN, Infinity |
'number' |
string |
'hola', "hola", `hola ${nombre}` |
'string' |
boolean |
true, false |
'boolean' |
undefined |
variable sin valor asignado | 'undefined' |
null |
«sin valor» puesto a propósito | 'object' |
bigint |
9007199254740993n |
'bigint' |
symbol |
identificadores únicos | 'symbol' |
| objeto | {}, [], funciones, fechas |
'object' o 'function' |
Tres diferencias con Java que conviene tener claras desde ya:
- No hay
int,doublenifloat: todo número es unnumberde coma flotante de 64 bits. Por eso0.1 + 0.2da0.30000000000000004, igual que undoubleen Java. - Hay dos «vacíos»:
undefinedes lo que obtienes cuando algo no tiene valor (una variable sin inicializar, una propiedad que no existe);nulles un valor que tú asignas para decir «vacío a propósito». typeof nulldevuelve'object'. Es un error histórico del lenguaje que nunca se ha corregido para no romper la web existente.
Los textos pueden escribirse con comillas simples, dobles o acentos graves. Estos últimos son template literals y permiten insertar expresiones: `Tienes ${edad} años`. Compara cómo queda el mismo mensaje concatenando y con un template literal:
const nombre = 'Ana';
const edad = 20;
'Hola, ' + nombre + '. Tienes ' + edad + ' años.'; // concatenando con +
`Hola, ${nombre}. Tienes ${edad} años.`; // 'Hola, Ana. Tienes 20 años.'
// Dentro de ${} vale cualquier expresión, no sólo variables
`El año que viene tendrás ${edad + 1} años`; // '... tendrás 21 años'
`Eres ${edad >= 18 ? 'mayor' : 'menor'} de edad`; // 'Eres mayor de edad'
// Y admiten saltos de línea sin caracteres especiales
const aviso = `Pedido confirmado
Llegará en ${2 + 1} días`;Lo que va entre ${ y } se evalúa y se convierte en texto. Sólo funciona con acentos graves: 'Tienes ${edad} años', con comillas simples, se muestra tal cual, con el ${edad} escrito.
3 // const, valores y referencias
Con los tipos delante ya se puede deshacer una de las confusiones más habituales. const no significa inmutable: significa que la variable no se puede reasignar, es decir, que no puede pasar a guardar otra cosa. Lo que eso implica depende de qué guarda la variable, y ahí entran dos ideas:
- Valor. Con un primitivo (número, texto, booleano...), la variable guarda el dato mismo. Además, los primitivos son inmutables: ninguna operación cambia el
5o el texto'hola'; lo que hacen es producir un valor nuevo. - Referencia. Con un objeto o un array, la variable no guarda el objeto, sino una referencia a él: algo parecido a la dirección del sitio de la memoria donde está. El objeto vive aparte, y se puede modificar por dentro.
Traducido a código:
const iva = 0.21;
iva = 0.10; // TypeError: Assignment to constant variable.
const notas = [7, 9];
notas.push(5); // válido: notas es ahora [7, 9, 5]
notas[0] = 10; // válido: notas es ahora [10, 9, 5]
notas = [1, 2]; // TypeError: intentaría apuntar a otro array
const alumno = { nombre: 'Ana' };
alumno.nombre = 'Eva'; // válido: cambia una propiedad del objeto
alumno = { nombre: 'Eva' }; // TypeError: sería otro objetoLa misma diferencia aparece al copiar una variable en otra, aunque no haya ningún const de por medio. Con un primitivo se copia el valor; con un objeto se copia la referencia, así que las dos variables acaban llegando al mismo objeto:
let a = 5;
let b = a; // b recibe una copia del 5
b = 8;
console.log(a); // 5: a no se ha enterado
const lista = [1, 2];
const otra = lista; // otra recibe la misma referencia, no un array nuevo
otra.push(3);
console.log(lista); // [1, 2, 3]: sólo hay un array| Aspecto | Primitivo | Objeto o array |
|---|---|---|
| Qué guarda la variable | El valor | Una referencia al objeto |
Qué copia b = a |
El valor: dos datos independientes | La referencia: dos nombres, un objeto |
Qué impide const |
Cambiar el valor | Apuntar a otro objeto |
| Se puede modificar por dentro | No, nunca | Sí, aunque la variable sea const |
Qué compara === |
Los valores | Si es el mismo objeto |
Si de verdad necesitas que un objeto no cambie, existe Object.freeze(objeto), que impide añadir, borrar o modificar sus propiedades. Sólo congela el primer nivel: si una propiedad es a su vez un array, ese array se sigue pudiendo modificar.
const protege la variable, no el valor. Con un primitivo, en la práctica es lo mismo; con un objeto, el contenido sigue siendo modificable. Cómo copiar objetos de verdad lo verás en la unidad de objetos, arrays y referencias.
4 // Tipado dinámico y coerción
Como el tipo va con el valor, una misma variable puede contener un número y después un texto. Eso es el tipado dinámico. La consecuencia importante no es esa, sino lo que ocurre cuando un operador recibe tipos que no esperaba: JavaScript no lanza un error, sino que convierte uno de los valores. Esa conversión automática se llama coerción.
Las reglas que más vas a encontrar:
+con algún texto concatena:'Total: ' + 10da'Total: 10'.-,*,/y las comparaciones<,>convierten a número:'10' * 2da20.- Si la conversión a número no tiene sentido, el resultado es
NaN(Not a Number), que además tiene la rareza de no ser igual a sí mismo: para comprobarlo se usaNumber.isNaN(valor).
Y la más importante: existen dos igualdades. == convierte los tipos antes de comparar, con reglas difíciles de recordar ('' == 0 es true, null == undefined es true). === compara valor y tipo, sin convertir nada.
Usa siempre === y !==. Cuando necesites convertir, hazlo tú de forma explícita con Number(texto), String(numero) o Boolean(valor), para que quien lea el código vea la conversión.
5 // Truthy y falsy
Una condición (if, while, &&, ||) no necesita un booleano: acepta cualquier valor y lo convierte. Sólo hay un puñado de valores falsy, que se convierten en false. Todo lo demás es truthy, y se convierte en true:
| Tipo | Falsy | Truthy |
|---|---|---|
| Booleanos | false |
true |
| Números | 0, -0, NaN |
-1, 0.5, Infinity |
| BigInt | 0n |
1n, -5n |
| Textos | '' (texto vacío) |
' ' (un espacio), '0', 'false' |
| Vacíos | null, undefined |
— |
| Objetos | — | [], {}, cualquier función |
Fíjate en la columna de la derecha: un texto sólo es falsy si está vacío, da igual lo que ponga dentro, y un objeto nunca es falsy, aunque no tenga nada. Para ver cómo se convierte un valor sin escribir un if, usa Boolean(valor):
Boolean(0); // false
Boolean('0'); // true: es un texto con un carácter
Boolean('false'); // true: es un texto, no el booleano
Boolean([]); // true: todo objeto es truthy
Boolean(null); // falseDonde más se nota es en las condiciones. Estos dos casos aparecen en casi cualquier formulario o listado:
const nombre = campoNombre.value; // '' si el usuario no escribe nada
if (nombre) {
console.log(`Hola, ${nombre}`);
} else {
console.log('Escribe tu nombre');
}
const resultados = [];
if (resultados) {
console.log('Hay resultados'); // se muestra siempre: [] es truthy
}
if (resultados.length) {
console.log('Hay resultados'); // sólo si hay elementos: 0 es falsy
}Esto permite escribir condiciones cortas, pero también esconde errores. El operador ?? (nullish coalescing) ayuda en el caso más frecuente: dar un valor por defecto sólo cuando el valor es null o undefined, sin tratar el 0 o el texto vacío como ausencia.
Una tienda guarda el número de unidades en carrito y quiere mostrar 1 si el dato no existe. El código original es const unidades = guardado || 1;.
- Si
guardadoesundefined:undefinedes falsy, así que||devuelve1. Correcto. - Si
guardadoes3:3es truthy,||devuelve3. Correcto. - Si
guardadoes0(el usuario vació el carrito):0es falsy, así que||devuelve1. Incorrecto. - Con
guardado ?? 1:??sólo sustituyenullyundefined, así que el0se respeta.
??: const unidades = guardado ?? 1;. || sirve cuando cualquier valor falsy significa «no hay dato»; ??, cuando 0, '' o false son datos válidos.6 // Para practicar
Tres ejercicios cortos para practicar template literals, conversiones explícitas y operaciones con números que no son enteros. Cada ejercicio trae el enunciado en los comentarios del código.
Saludos con template literalstraining-daw-sv/modulo-00/02-tipos-dinamicos-y-coercion/01_template_stringsCalculadora y coerción de tipostraining-daw-sv/modulo-00/02-tipos-dinamicos-y-coercion/02_calculatorConversor de segundostraining-daw-sv/modulo-00/02-tipos-dinamicos-y-coercion/03_seconds_converterAutoevaluación
En Java, int edad = 20; edad = "veinte"; no compila. En JavaScript el equivalente funciona. ¿Qué diferencia de diseño hay detrás?
¿Por qué '5' + 3 da '53' pero '5' - 3 da 2?
+ sirve para sumar y para concatenar, y si uno de los operandos es texto concatena. El - sólo tiene sentido con números, así que convierte '5' en 5 antes de operar.Si const lista = [] es una constante, ¿cómo es posible que lista.push(1) funcione?
const impide reasignar la variable, no modificar el valor. lista sigue apuntando al mismo array; lo que cambia es el contenido de ese array. Lo que daría error es lista = [1], que intenta apuntar la variable a otro valor.Un formulario lee una cantidad con input.value y el código hace if (cantidad). Con la cantidad 0 el if sí entra. ¿Por qué?
input.value siempre es un texto, y '0' es truthy. Sólo la cadena vacía es falsy entre los textos. El número 0 sería falsy, pero aquí no hay número hasta que lo conviertas con Number(input.value).