Un valor por defecto más inteligente
JavaScript siempre ha tenido un atajo para asignar valores por defecto: value || fallback. Funciona casi siempre, pero arrastra un problema de fábrica: considera "ausente" cualquier valor falsy. Es decir, 0, '' y false disparan el fallback aunque sean la respuesta correcta.
El operador de nullish coalescing ?? viene a resolver justo eso. Solo recurre al valor por defecto cuando el original es null o undefined:
0 y '' sobreviven. null y undefined no. Ese es el operador entero.
Por qué || se queda corto
Este es el bug clásico que ?? vino a solucionar:
El usuario pidió volumen 0 (silencio) y un nick vacío. Los dos valores fueron pisados sin avisar porque || no distingue entre "no hay valor" y "valor falsy".
Cambia a ??:
Ahora el 0 y el '' pasan sin que nadie los toque, y solo los campos que realmente faltan acaban tomando el valor por defecto. Esto es, casi siempre, lo que buscas.
El modelo mental
?? se hace una única pregunta: ¿el operando de la izquierda es null o undefined? Ninguna otra cosa cuenta.
| Valor | izq || der devuelve | izq ?? der devuelve |
|---|---|---|
null | der | der |
undefined | der | der |
0 | der | izq (0) |
'' | der | izq ('') |
false | der | izq (false) |
NaN | der | izq (NaN) |
| cualquier objeto | izq | izq |
Usa ?? cuando tus datos puedan tener valores falsy que sí significan algo. Usa || cuando de verdad quieras descartar todo lo falsy (cadenas vacías, contadores en cero, etc.) — ese caso existe, pero es menos habitual de lo que solemos pensar.
Evaluación en cortocircuito
Igual que || y &&, el operador ?? evalúa en cortocircuito. Si el operando de la izquierda no es nullish, el de la derecha ni siquiera se ejecuta:
expensiveDefault() se ejecuta una sola vez: solo para b. Esto resulta muy útil cuando tu valor por defecto es una llamada a función, una petición de red o cualquier otra operación que prefieres evitar si no hace falta.
Combinando con optional chaining
Los operadores ?? y ?. aparecieron juntos en ES2020 y están pensados para trabajar en equipo. El optional chaining recorre una ruta que podría no existir y devuelve undefined si alguno de los pasos falta; después, el nullish coalescing se encarga de poner un valor por defecto razonable:
Sin esta pareja, el mismo código se convierte en una cadena fea de && o en un try/catch. Con ellos, el acceso seguro y los valores por defecto caben en una sola línea.
Asignación nullish: ??=
a ??= b asigna b a a solo cuando a es null o undefined. Es el operador de asignación lógica nullish, y usa evaluación en cortocircuito: si a ya tiene un valor, la expresión de la derecha ni siquiera se evalúa.
Fíjate en que verbose: false y retries: 0 se conservan: ??= solo rellena lo que realmente falta. Compáralo con ||=, que pisaría ambos valores.
Precedencia y la regla de los paréntesis
El operador ?? se niega a propósito a mezclarse con || o && sin paréntesis. Esto lanza un SyntaxError:
const x = a || b ?? c; // SyntaxError
Quienes diseñaron el lenguaje consideraron que la precedencia era lo bastante ambigua como para obligarte a dejarlo claro. Basta con añadir paréntesis y listo:
Agrupaciones distintas, resultados distintos. Los paréntesis no son decorativos: le están diciendo a quien lea tu código mañana (y al parser) qué era lo que querías expresar.
No es lo mismo que un parámetro por defecto
Los parámetros por defecto en la firma de una función tienen su propia lógica: solo se activan cuando el argumento es undefined, nunca cuando es null. Ahí está la diferencia sutil con ??, que trata ambos casos por igual.
Si también quieres que null dispare el valor por defecto, usa ?? dentro del cuerpo:
Pequeña diferencia, pero fácil de pasar por alto cuando una API te devuelve null para indicar "sin valor".
¿Cuándo usar el operador ?? en JavaScript?
Toma ?? como tu operador por defecto, salvo que tengas un motivo concreto para no hacerlo. Es el que mejor encaja con el comportamiento real de los datos: 0, '' y false suelen ser valores legítimos que conviene conservar, y solo los datos realmente ausentes (null o undefined) merecen un valor por defecto. Deja || para esos casos puntuales en los que sí quieres reemplazar cualquier valor falsy.
Siguiente paso: clases
Con esto cerramos el bloque de objetos y arrays: ya tienes las herramientas para construir y recorrer estructuras de datos sin llevarte sustos. En el próximo capítulo damos el salto a organizar el comportamiento: clases, herencia y el sistema de prototipos que hay por debajo.
Preguntas frecuentes
¿Qué es el operador de nullish coalescing en JavaScript?
?? devuelve el operando de la derecha únicamente cuando el de la izquierda es null o undefined. En cualquier otro caso, deja pasar el valor original. La expresión value ?? fallback es la forma estándar de asignar un valor por defecto sin tropezar con valores falsy que sí son válidos, como 0 o ''.
¿Cuál es la diferencia entre ?? y || en JavaScript?
|| recurre al fallback con cualquier valor falsy: 0, '', false, NaN, null y undefined. ?? solo lo hace con null y undefined. Si en tus datos 0 o una cadena vacía son valores legítimos, usa ??. Si lo que realmente quieres es descartar cualquier valor falsy, || sigue siendo la opción correcta.
¿Se puede combinar ?? con optional chaining?
Sí, de hecho es una de las combinaciones más habituales. user?.settings?.theme ?? 'light' recorre la cadena de forma segura, devuelve undefined si algo falta por el camino y entonces aplica 'light'. Ambos operadores llegaron juntos en ES2020 precisamente para cubrir este patrón.
¿Qué hace el operador ??=?
a ??= b asigna b a a solo si a vale null o undefined. Es la asignación lógica nullish, una forma corta de a = a ?? b, pero con cortocircuito: si a ya tiene valor, ni siquiera se evalúa la parte derecha. Muy útil para rellenar defaults que faltan en un objeto de opciones.