Aunque pueda parecer que comparar dos valores es algo sencillo y común, lo cierto es que comparar en Javascript conlleva algunas dificultades que debemos conocer y manejar para evitar problemas en nuestro código. Una de estas características singulares es la diferencia entre la comparación estricta y no estricta, es decir, la diferencia entre ==
(con sólo dos signos de igualdad) y ===
(con sólo tres signos de igualdad).
Antes de avanzar tenemos que recordar que Javascript es un lenguaje con un tipado débil, es decir, los valores tienen un tipo, pero este puede ser cambiado de forma implícita en cualquier momento. De esta forma, si escribimos algo como 1 + "0"
obtendremos una cadena con el valor "10"
. La clave para comprender la diferencia entre las dos formas de hacer las comparaciones ==
(con sólo dos signos de igualdad) y ===
(con sólo tres signos de igualdad) es la conversión de tipos.
Se suele considerar que es conveniente utilizar la comparación estricta, pero veamos con más detenimiento como funcionan y que ventajas e inconvenientes tenemos en cada caso.
Comparación no estricta o con conversión de tipo
La comparación con ==
o con !==
es denominada como «comparación no estricta», ya que realiza conversiones de tipo. Si los dos datos que estamos comparando no son del mismo tipo, se realizan una serie de conversiones de tipo a fin de encontrar una coincidencia con alguna de las combinaciones que internamente evalua.
El problema es que estas conversiones no son siempre muy evidentes y nos pueden desconcertar. Por ejemplo, las siguientes expresiones son todas verdaderas, aunque pueda parecer que no debería ser así:
console.assert( 1 == true ); console.assert( "1" == true ); console.assert( [1] == true ); console.assert( "" == false ); console.assert( "0" == false ); console.assert( 0 == false ); console.assert( [0] == false ); console.assert( [ ] == false ); console.assert( [[]] == false ); console.assert( 0 == "" ); console.assert( 0 == "0" ); console.assert( 1 == "1" ); console.assert( [ ] == 0 ); console.assert( [[]] == 0 ); console.assert( [0] == 0 ); console.assert( [ ] == "" ); console.assert( [[]] == "" ); console.assert( null == undefined ); console.assert( ',,,' == new Array(4) );
Nota: estamos utilizando console.assert()
, método que explicamos al describir el sistema de pruebas minimalista en Javascript que vamos a utilizar.
Seguramente algo como 1 == true
podemos aceptarlo, ya que ambos son valores que se suelen denominar como truthy (que se consideran como vedaderas), y las usamos muy habitualmente, por ejemplo, dentro de instrucciones if ()
. Aunque no nos estemos dando cuenta, cuando consideramos que 1
es verdadero y 0
es falso en un if ()
, estamos aprovechando una conversión implícita de tipo.
Seguramente nos sintamos más confundidos a la hora de comparar cadenas y números, ya que 1 == "1"
no parece que deba ser verdadero, pero esta conversión de tipos es más o menos previsible.
Lo que es muy difícil de comprender es que algo como [[]]
sea igual a 0
o ',,,'
sea igual que new Array(4)
. Estos caprichos de la conversión de tipos en Javascript ha dado lugar una buena colección de chistes, presentaciones y divertimentos, además de problemas graves en más de un programa en producción.
En muchos casos los analizadores de código dan un aviso o un error cuando encuentran un ==
advirtiendo que quizás hemos querido poner ===
y por un descuido nos hemos saltado un signo de igual. Pero en ocasiones utilizar la conversión de tipo puede ser útil y es algo conveniente, pero es cierto que un pequeño descuido puede hacer cambiar sustancialmente el comportamiento de nuestro programa.
Comparación estricta o sin conversión de tipos
En general se considera que la comparación estricta con ===
o !==
es más conveniente y segura. Este tipo de comparación no realiza ningún tipo de conversión de tipo y nos asegura que coinciden tanto el valor como el tipo de los dos elementos que estamos comparando. Si que comparamos datos de tipos diferentes, siempre se producirá un resultado negativo. En ocasiones se dice que este tipo de comparaciones comprueba tipo y valor, aunque realmente lo que hace es no realizar ningún tipo de conversión.
Todos los casos que vimos antes ahora devuelven false
, por lo que para que console.assert()
nos acepte la instrucción que queremos comprobar hemos utilizado la negación estricta !==
.
console.assert( 1 !== true ); console.assert( "1" !== true ); console.assert( [1] !== true ); console.assert( "" !== false ); console.assert( "0" !== false ); console.assert( 0 !== false ); console.assert( [0] !== false ); console.assert( [ ] !== false ); console.assert( [[]] !== false ); console.assert( 0 !== "" ); console.assert( 0 !== "0" ); console.assert( 1 !== "1" ); console.assert( [ ] !== 0 ); console.assert( [[]] !== 0 ); console.assert( [0] !== 0 ); console.assert( [ ] !== "" ); console.assert( [[]] !== "" ); console.assert( null !== undefined ); console.assert( ',,,' !== new Array(4) );
Función de comparación que permita ambos casos
Como hemos comentado, estamos creando una función universal que permita la comparación de cualquier tipo de datos y tenemos que tener en cuenta estos dos tipos de comparaciones. Aunque por defecto useremos una comparación estricta, es cierto que en ocasiones nos puede interesar hacer una comparación con conversión de tipos, pero siempre dejando claro que es algo intencionado y no involuntario, como puede ser el olvidarse de un =
escribiendo ==
en vez de ===
por un sencillo descuido.
La idea es que nuestra función realize una la comparación estricta por defecto de esta forma:
function equal(a, b) { if (a === b) { return true; } return false; }
Segunda versión de esqueal.js
en GitHub y su juego de pruebas.
Para incorporar la posibilidad de hacer una comparación no estricta tenemos que incluir un parámetro opcional (que como luego será ampliado, lo hemos definido como un objeto con varias opciones).
Para poder dar una respuesta precisa a la hora de hacer la comparación hemos incluido diferentes valores de retorno: 0
cuando no es igual, 1
cuando es igual el valor y 2
cuando coinciden valor y tipo.
function equal(a, b, options) { options = options || {}; // Optional parameter if (a === b) { // Strict comparison return equal.VALUE_AND_TYPE; // Equal value and type } if (options.nonStrict && a == b) { // Non strict comparison (optional) return equal.VALUE; // Equal value (different type) } return equal.NOT_EQUAL; // Not equal } equal.NOT_EQUAL = 0; equal.VALUE = 1; equal.VALUE_AND_TYPE = 2;
Tercera versión de esqueal.js
en GitHub y sus pruebas.
Se ha incluido los valores equal.NOT_EQUAL
, equal.VALUE
y equal.VALUE_AND_TYPE
como propiedades de la función equal()
. Las funciones son objetos y aceptan propiedades, por lo que son una buena opción para almacenar los valores utilizados por esa misma función como retorno. No hemos utilizado const
ya que hemos decidido que el programa debe poder funcionar con cualquier navegador o versión de Node que soporte EcmaScript 5.1 y esta const
una instrucción de ES6.
Aunque la funcionalidad implementada sigue siendo muy limitada, muestra algunos avances y permite comparar de forma estricta y no estricta sin posibilidad de equívoco, devolviendo valores consistentes y precisos.
console.assert( !equal( true, 1 ) ); console.assert( equal( true, 1, {nonStrict: true} ) ); console.assert( equal( true, 1, {nonStrict: true} ) === equal.VALUE ); console.assert( equal( true, 1, {nonStrict: true} ) !== equal.VALUE_AND_TYPE );
Conclusiones
No debemos considerar la comparación no estricta como el demonio, es útil, pero debemos estar seguros que es lo que queríamos hacer y no escribir por descuido ==
cuando queríamos escribir ===
.
Nuestra función de comparación universal de valores en Javascript todavía carece de una funcionalidad realmente interesante, pero va tomando cuerpo y en este recorrido nos permite repasar algunas características interesantes del lenguaje. De momento ya incluye ambos tipos de comparación, estricta y no estricta. En los próximos capítulos avanzaremos de forma significativa, pero, si no puedes esperar, descarga la versión final de esqueal.js con npm.
Novedades
HTTP2 para programadores. Enviar mensajes del servidor al cliente con Server Sent Event (sin WebSockets)
En esta charla, organizada por MadridJS, Pablo Almunia nos muestra cómo la mayoría de nosotros cuando oímos hablar por primera vez de HTTP2 nos ilusionamos con las posibilidades que presumiblemente se abrían para el desarrollo de soluciones web avanzadas y cómo muchos nos sentimos defraudados con lo que realmente se podía implementar.
En esta charla podemos ver cómo funciona el HTTP2, que debemos tener en cuenta en el servidor para hace uso de este protocolo y, sobre todo, cómo podemos enviar información desde el servidor al cliente de forma efectiva y fácil. Veremos con detenimiento cómo por medio de los Server-Sent Events (SSE) podemos recibir en el cliente datos enviados desde el servidor sin utilizar websocket, simplificando enormemente la construcción de aplicaciones con comunicación bidireccional.
Observables en Javascript con Proxies
En esta charla, organizada por MadridJS, Pablo Almunia nos habla de la observación reactiva de objetos en Javascript por medio de Proxy. Se describe paso a paso cómo funcionan los Proxies y en que casos pueden ser nuestro mejor aliado. Veremos que no hay que tenerles miedo, son bastante sencillos de utilizar, y nos ofrecen una gran abanico de posibilidades.
Aplicaciones JAMStack, SEO friendly y escalables con NextJS
En esta charla de Madrid JS, Rafael Ventura nos describe las funcionalidades clave de NextJS, nos muestra en vivo cómo desarrollar una completa aplicación JAMStack con Server Side Rendering (SSR) y Static Site Generation (SSG) y termina mostrando como publicar esta aplicación en Vercel.
Stencil JS: mejora el Time To Market de tu producto, por Rubén Aguilera
En esta charla Rubén Aguilera nos cuenta los problemas que tienen muchas empresas a la hora de sacar productos accesibles, vistosos y usables en el Time To Market que requiere Negocio y cómo podemos minimizar este tiempo gracias al DevUI con StencilJS para adecuar una aplicación de Angular a las exigencias del mercado en tiempo record.
breves
Javascript: 25 aniversario
25 años con Javascript han dado para mucho. Pero todavía queda mucho para este lenguaje de programación.
Descrubir algunas características de console
En el día a día nos encontramos muy a menudo utilizando console. Es una navaja multiusos que nos facilita la vida a la hora de depurar nuestro código. La mayoría de nosotros ha utilizado console.log(), pero tiene otras muchas funcionalidades.
Matrices dispersas o sparse arrays en JS
Una característica que puede producir algunos problemas, si no lo tenemos en cuenta, es la posibilidad de tener matrices con huecos, es decir, con algunos de sus elementos sin definir. Es lo que se suele denominar una matriz dispersa o sparse array. Veamos cómo trabajar con esta características de las matrices.
Operadores de bits usados con asiduidad
Cada día más a menudo podemos encontrar operadores binarios utilizados como formas abreviadas de algunas operaciones que de otra forma sería algo menos compactas y, quizás, más comprensibles. Veamos algunos casos en detalle.
Var a = new String();
a = ‘hola’;
a===’hola’ // false
Var a = new String();
a===’’ // false
new String() no crea una cadena, crea un objeto wrapper sobre una cadena. Por eso hace estas cosas tan «extrañas».
var a = new String(«Hola»);
a===»hola» // false
y
var a = new String(«»);
a===»» // false
Si utilizamos == se realiza una conversión desde objeto a cadena, y entonces funciona. No obstante, poner new antes de String() no es una buena idea.