Operadores de Comparación e Igualdad en JavaScript
Entiende la comparación de valores con ===, ==, >, <, >=, <=. Aprende por qué === es más seguro que ==, y evita bugs comunes de comparación.
TL;DR - Resumen rápido
- === (igualdad estricta) compara valor Y tipo sin coerción. == (igualdad suelta) hace coerción de tipos
- SIEMPRE usa === y !== en lugar de == y != para evitar bugs de coerción inesperada
- Operadores relacionales: > < >= <= comparan números. Con strings, comparan alfabéticamente (Unicode)
- null == undefined es true, pero null === undefined es false. Son los únicos valores == sin ser ===
- Objetos y arrays se comparan por referencia, no por contenido. {} === {} es false (referencias diferentes)
- NaN no es igual a nada, ni a sí mismo: NaN === NaN es false. Usa Number.isNaN() para verificar NaN
- Object.is() es como === pero distingue -0 de +0, y considera NaN === NaN true
Introducción a Operadores de Comparación
Los operadores de comparación permiten comparar dos valores y retornan un booleano (true o false). JavaScript tiene operadores de igualdad (===, ==, !==, !=) que verifican si dos valores son iguales o diferentes, y operadores relacionales (>, <, >=, <=) que verifican orden o magnitud. Estos operadores son fundamentales para condiciones, validaciones, y control de flujo en tu código.
La distinción más importante es entre igualdad estricta (===) e igualdad suelta (==). La igualdad estricta compara valor Y tipo sin hacer conversiones: 5 === "5" es false porque son tipos diferentes. La igualdad suelta hace coerción de tipos antes de comparar: 5 == "5" es true porque convierte el string a número. Esta coerción causa bugs sutiles, por eso la mejor práctica universal es SIEMPRE usar === y nunca usar ==.
Por Qué Existen Dos Tipos de Igualdad
JavaScript heredó == de lenguajes antiguos donde la coerción automática era común. === se añadió después para permitir comparación sin coerción, más predecible. Hoy en día, == se considera un error de diseño histórico: casi nunca quieres comparación con coerción. En JavaScript moderno, trata == como obsoleto y usa === siempre.
Igualdad Estricta (===): Sin Coerción
El operador de igualdad estricta (===) compara dos valores sin hacer conversión de tipos. Retorna true solo si ambos valores tienen el mismo tipo Y el mismo valor. 5 === 5 es true, pero 5 === "5" es false porque son tipos diferentes. También existe !== que retorna true si los valores son diferentes en valor O en tipo. Esta es la forma segura y predecible de comparar valores.
Con ===, los valores deben ser del mismo tipo y tener el mismo valor para ser iguales. Números se comparan por valor, strings por contenido carácter por carácter, booleanos por valor. null solo es === a null, undefined solo es === a undefined. NaN no es === a nada, ni siquiera a sí mismo. Objetos y arrays se comparan por referencia: dos objetos con mismo contenido no son === si son instancias diferentes.
=== Es La Forma Correcta de Comparar
La mejor práctica universal en JavaScript moderno es SIEMPRE usar === y !== para comparaciones. Son más rápidos (sin coerción), más predecibles, y previenen bugs sutiles. Si quieres verificar null O undefined juntos, usa explícitamente: value === null || value === undefined. ESLint te avisará si usas ==. Trata === como el único operador de igualdad que existe.
Igualdad Suelta (==): Con Coerción
El operador de igualdad suelta (==) compara dos valores después de convertirlos a un tipo común. JavaScript tiene reglas complejas de coerción para ==: strings se convierten a números, booleanos se convierten a números (true es 1, false es 0), null == undefined es true por regla especial. Estas conversiones automáticas causan comportamientos contraintuitivos y bugs sutiles. Por eso == se considera mala práctica y debes evitarlo.
El operador == hace coerción con reglas complejas: si los tipos son iguales, compara como ===; null == undefined es true, pero no son == a otros valores; si uno es número y otro string, convierte el string a número; si uno es boolean, lo convierte a número (true=1, false=0); y los objetos se convierten con valueOf() o toString(). Estas reglas causan resultados sorprendentes como "0" == false siendo true. Evita == completamente.
Por Qué == Es Peligroso
Las reglas de coerción de == son tan complejas que incluso programadores experimentados se equivocan. "0" == false es true, "0" == 0 también es true, y false == 0 es true, pero además [] == false es true. Estos comportamientos inconsistentes causan bugs difíciles de detectar. No hay ventaja en usar ==: solo añade complejidad y riesgo. Usa === siempre.
=== vs ==: Diferencias Clave y Cuándo Usar Cada Uno
La diferencia fundamental es que === compara sin coerción de tipos, mientras == hace coerción antes de comparar. === es más estricto, más rápido, más predecible. == es más permisivo pero impredecible. En código profesional moderno, se usa === exclusivamente. El único caso histórico donde algunos usan == es para verificar null y undefined juntos (value == null), pero incluso esto es mejor hacerlo explícitamente con === y ||.
Diferencias principales entre === y ==:
- <code>===</code> compara tipo y valor; <code>==</code> solo valor después de coerción
- <code>===</code> es más rápido (sin conversiones); <code>==</code> es más lento
- <code>===</code> es predecible; <code>==</code> tiene reglas de coerción complejas
- <code>===</code> nunca causa bugs de coerción; <code>==</code> es fuente común de bugs
- <code>===</code> es el estándar en código moderno; <code>==</code> se considera obsoleto
Operadores Relacionales: >, <, >=, <=
Los operadores relacionales (>, <, >=, <=) verifican si un valor es mayor, menor, mayor o igual, o menor o igual que otro. Con números, funcionan como esperarías: 10 > 5 es true. Con strings, comparan alfabéticamente usando valores Unicode: "b" > "a" es true. Si comparas tipos diferentes, JavaScript hace coerción a número, lo que puede dar resultados inesperados.
Los operadores relacionales funcionan bien con números. Con strings, comparan carácter por carácter usando valores Unicode (case-sensitive: "A" < "a" porque mayúsculas tienen códigos menores). Si comparas número con string, convierte a número: "10" > 5 es true. >= y <= combinan comparación con igualdad: 5 >= 5 es true. Con tipos mixtos, la coerción puede dar resultados inesperados: valida tipos antes de comparar.
Comparación de Strings: Unicode, No Alfabética
Los strings se comparan por código Unicode, no alfabéticamente: "Z" < "a" es true (mayúsculas antes que minúsculas). Para comparación case-insensitive, usa .toLowerCase(): str1.toLowerCase() > str2.toLowerCase(). Para comparación localizada (con acentos, ñ, etc.), usa localeCompare(): str1.localeCompare(str2). Unicode hace que "10" < "2" sea true (compara carácter por carácter: "1" < "2").
Comparación de Strings: Unicode y Casos Especiales
La comparación de strings en JavaScript se basa en valores Unicode (UTF-16), no en orden alfabético tradicional. Cada carácter tiene un código numérico, y la comparación se hace carácter por carácter de izquierda a derecha. Esto causa comportamientos inesperados: mayúsculas vienen antes que minúsculas, números como strings se ordenan lexicográficamente (no numéricamente), y caracteres especiales tienen posiciones específicas en la tabla Unicode.
Los strings se comparan carácter por carácter usando códigos Unicode. Mayúsculas (65-90) vienen antes que minúsculas (97-122), por eso "Z" < "a" es true. Números como strings se comparan lexicográficamente: "10" < "2" porque compara primero "1" vs "2". Para comparación case-insensitive, convierte a minúsculas con .toLowerCase(). Para comparación numérica, usa Number(). Para comparación localizada (alfabetos con acentos), usa localeCompare().
Comparación de Objetos: Por Referencia, No por Contenido
Los objetos y arrays se comparan por referencia, no por contenido. Dos objetos son iguales solo si son la MISMA instancia (apuntan a la misma ubicación en memoria). Dos objetos con propiedades idénticas no son === si son instancias diferentes. Esto es diferente a primitivos que se comparan por valor. Para comparar contenido de objetos, necesitas comparación profunda manual.
Objetos y arrays se comparan por referencia. {} === {} es false porque son dos instancias diferentes, aunque tengan el mismo contenido. Solo si dos variables apuntan al MISMO objeto son ===. Esto aplica a arrays, funciones, fechas, y cualquier objeto. Para comparar contenido de objetos simples, JSON.stringify() puede funcionar como solución rápida, pero falla con funciones, undefined, símbolos, y si las propiedades están en distinto orden.
Casos Especiales: NaN, null, undefined, -0
JavaScript tiene varios casos especiales en comparaciones que pueden causar confusión. NaN (Not a Number) no es igual a nada, ni siquiera a sí mismo: NaN === NaN es false. null y undefined son == pero no ===. -0 y +0 son === aunque sean valores diferentes. Infinity es === a Infinity. Estos casos especiales requieren métodos específicos de verificación.
Los casos especiales más importantes que debes conocer son:
- <code>NaN === NaN</code> es <code>false</code>: usa <code>Number.isNaN()</code> para verificar NaN
- <code>null == undefined</code> es <code>true</code>, pero <code>null === undefined</code> es <code>false</code>
- <code>-0 === +0</code> es <code>true</code> aunque matemáticamente sean diferentes
- <code>Infinity === Infinity</code> es <code>true</code>
- Objetos vacíos nunca son iguales: <code>{} === {}</code> es <code>false</code>
NaN: El Valor Que No Se Iguala a Sí Mismo
NaN es único porque no es igual a nada, ni a sí mismo: NaN === NaN retorna false. Esto viene de la especificación IEEE 754 para números flotantes. No puedes usar === para verificar NaN: debes usar Number.isNaN(valor). El método global isNaN() es diferente y menos confiable: convierte a número primero, así que isNaN("hola") es true. Usa Number.isNaN() que solo retorna true para NaN real.
Object.is(): Comparación Más Estricta que ===
Object.is() es un método moderno (ES6) que hace comparación similar a === pero con dos diferencias: distingue +0 de -0 (Object.is(+0, -0) es false), y considera NaN igual a NaN (Object.is(NaN, NaN) es true). Es la comparación más estricta y matemáticamente correcta de JavaScript. Úsalo cuando necesites distinguir estos casos edge, pero para la mayoría de comparaciones === es suficiente.
Object.is() es como === pero más correcto matemáticamente: distingue +0 de -0 (mientras === los trata como iguales), y considera NaN === NaN como true (mientras === lo trata como false). En todo lo demás funciona igual que ===. Úsalo cuando estos casos edge importan (cálculos científicos, claves de Map). Para comparaciones normales, === es suficiente. Object.is() no hace coerción de tipos como ==.
Resumen: Operadores de Comparación
Operadores principales:
- •=== !== para igualdad estricta sin coerción (SIEMPRE usa estos)
- •== != para igualdad suelta con coerción (NUNCA uses estos)
- •> < >= <= para comparar magnitud de números y orden de strings
- •Object.is() para comparación perfecta que distingue +0/-0 y NaN
- •Objetos se comparan por referencia, no por contenido
Mejores prácticas:
- •SIEMPRE usa === y !==, NUNCA uses == o !=
- •Para NaN usa Number.isNaN(), no comparación directa
- •Para null/undefined usa === null o === undefined explícitamente
- •Strings se comparan por Unicode, usa localeCompare() para alfabético
- •Para comparar objetos por contenido, usa comparación manual o JSON.stringify()