Cuando trabajamos con datos en Javascript nos encontramos con algunos casos algo complicados de gestionar entre los que están undefined
, null
y NaN
. Con estos datos debemos tener en cuenta su especial comportamiento y naturaleza. Vamos a dar un repaso a estos tipos de datos con la excusa del desarrollo de una función universal de comparación de cualquier tipo de datos en Javascript que venimos desarrollando en esta serie de artículos.
undefined
Cuando una variable ha quedado sin definir tiene un valor especial denominado undefined
, que se podría traducir como «sin definir«. Este valor puede resultar un poco desconcertante, ya que además de un tipo de datos, existe una variable con ese nombre y también es un valor como tal.
En primer lugar undefined
es es un tipo de dato y por lo tanto se puede consultar con typeof
.
var variable; if (typeof variable === "undefined") { console.log('ok'); }
Aunque estrictamente no es parte de la especificación del lenguaje EcmaScript, en todas las implementaciones existe una variable global que contiene este valor y que se denomina también undefined
, por lo que podemos hacer comparaciones de este tipo:
var variable; if (variable === undefined) { console.log('ok'); }
Un error que se comete a menudo, y que no tienen ningún sentido, es comparar la variable undefined
con el resultado de typeof
. Esta comparación siempre devolverá false
, ya que typeof
siempre devolverá una cadena de texto con el tipo.
var variable; if (typeof variable === undefined) { console.log('ok'); // No se ejecuta }
Otro problema al comparar con la variable undefined
, es que si la variable que estamos analizando no ha sido definida con var
, let
o const
la comparación producirá un error. Por ello, siempre es recomendable utilizar typeof
para comprobar si se ha definido una determinada variable.
También tenemos que tener en cuenta que undefined
no es una palabra reservada de Javascript y por lo tanto podemos definir una variable con ese nombre. Si lo intentamos en el contexto global nos encontraremos que esa variable ha sido definida como {writable: false, configurable: false}
y no podemos cambiarla, pero sí podemos hacerlo dentro del alcance de una función, aunque sin duda es una muy mala idea:
function test() { var variable; var undefined = 10; if (variable === undefined) { console.log('ok'); // No se ejecuta } }
Por último, debemos recordar que el operador void
devuelve siempre un valor undefined
, sea cual sea la expresión que le situemos a su derecha y puede ser de utilidad en alguna ocasión. Lo cierto es que instrucciones como void(0)
son ahora poco habituales, pero son perfectamente válidas. En el caso anterior podríamos haber comparado con variable === void(0)
y hubiéramos obtenido un resultado correcto.
null
El caso del valor nulo es también bastante singular. En este caso null
es un literal definido en la especificación del lenguaje, no una variable global como en el caso undefined
, por lo tanto, es una palabra reservada y no podremos utilizarla como nombre de variable.
Podemos directamente utilizar el literal null
para comprobar este valor es el que contiene una determinada variable o es el retorno de una función.
var variable = null; if (variable === null) { console.log('ok'); }
A diferencia de undefined
, null
no dispone de un tipo específico y en el caso de preguntar con typeof
sobre un elemento con valor null
nos dirá que es un objeto, lo cual puede desconcertarnos, ya que en la práctica no podemos tratarlo como al resto de objetos.
var variable = null; if (typeof variable === "object") { console.log('ok'); }
Que null
es un objeto es algo peculiar y debemos tener en considerar que una variable que dice ser un objeto realmente puede contener null
y producir un error en nuestro código si lo lo validamos.
NaN
Cuando trabajamos con valores numéricos tenemos que tener en cuenta la existencia de NaN
(Not a Number). Aunque realmente sí es un valor de tipo numérico, no podemos operar con él como con el resto de valores numéricos, ya que cualquier operación que se realice con NaN
siempre devolverá NaN
.
Normalmente se obtiene NaN
como resultado de las operaciones con funciones y métodos de Math
donde no ha sido posible convertir un valor a numérico o se ha producido algún error. Ejemplos sencillos en los que se obtiene este tipo de valor son:
Math.sqrt(-1); parseInt('no');
El problema a la hora de comprobar si un valor es NaN
es que si usamos typeof
nos dirá que es un valor numérico y podemos considerar que tenemos entre manos un número válido.
El segundo problema es que para comprobar si es un número no válido no podemos comparar con la variable global con nombre NaN
, ya que siempre obtendremos false
(NaN === NaN
siempre devuelve false
). Como consecuencia no podemos comparar NaN
de una forma natural.
var variable = parseInt('xxx'); if (typeof variable === 'number') { console.log('Is a number'); // Se ejecuta } if (variable === NaN) { console.log('Is NaN'); // No se ejecuta }
Para resolver este problema existe la función isNaN()
, y desde ES6 también la encontramos como Math.isNaN()
. Esta función devolverá verdadero si el valor pasado como parámetro es NaN
o no es un valor numérico. Esto complica bastante el trabajo con este tipo de valor.
var variable = parseInt('xxx'); if (isNaN(variable)) { console.log('Is NaN'); // Se ejecuta }
En este caso también tenemos que tener en cuenta que NaN
no es una palabra reservada de Javascript y por lo tanto podemos definir una variable con ese nombre. Igual que en el caso de undefined
, si intentamos cambiar esta variable en el contexto global nos encontraremos que ha sido definida como {writable: false, configurable: false}
y no podemos cambiarla, pero sí podemos hacerlo dentro del alcance de una función, aunque de nuevo es una muy mala idea:
function test() { var variable = 10; var NaN = 10; if (variable === NaN) { console.log('ok'); // Se ejecuta } }
Comparar null
o undefined
Para incorporar la comparación de null
o undefined
a nuestra función de comparación universal de cualquier tipo de valor en Javascript -que venimos desarrollando en esta serie de artículos- podemos aprovechar que ya se han realizado comparaciones estrictas y no estrictas entre los dos valores y por lo tanto, podemos tener en cuenta que cualquier valor comparado con null
o undefined
será siempre false
.
Para gestionarlo incluiremos algunas lineas a nuestra función equal()
:
function equal(a, b, options) { var aType = typeof a, bType = typeof b; // Get value types 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) } if (aType === 'undefined' || // undefined and null are always different than other values bType === 'undefined' || a === null || b === null) { return equal.NOT_EQUAL; } return equal.NOT_EQUAL; // Not equal } equal.NOT_EQUAL = 0; equal.VALUE = 1; equal.VALUE_AND_TYPE = 2;
Versión de esequal.js
en GitHub y sus pruebas.
Lo cierto es que en este estado no parece que hayamos avanzado mucho, ya que ya estábamos retornando equal.NOT_EQUAL
, pero lo cierto es que nos facilita el camino para avanzar con seguridad en los próximos pasos.
Comparar NaN
Para incorporar la comparación con NaN
en nuestra función debemos comprobar si es de tipo numérico y hacer uso de isNaN()
para los dos valores que estamos comparando, entonces debemos considerar que si en ambos casos devuelve true
, son equivalentes. Para ello incorporamos algunas líneas adicionales a nuestra función equal()
.
function equal(a, b, options) { var aType = typeof a, bType = typeof b; // Get value types 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) } if (aType === 'undefined' || // undefined and null are always different than other values bType === 'undefined' || a === null || b === null) { return equal.NOT_EQUAL; } if (aType === 'number' && // Special case: Not is a Number (NaN !== NaN) bType === 'number' && isNaN(a) && isNaN(b)) { return equal.VALUE_AND_TYPE; } return equal.NOT_EQUAL; // Not equal } equal.NOT_EQUAL = 0; equal.VALUE = 1; equal.VALUE_AND_TYPE = 2;
Versión de esequal.js
en GitHub y su juego de pruebas.
Conclusiones
Javascript está lleno de sorpresas. Nos encontramos datos como NaN
que tienen un comportamiento absolutamente singular y que tenemos que tener muy en cuenta para que nuestros programas se comporte adecuadamente. La existencia undefined
puede ser algo confusa, ya que es un tipo y también un dato. El hecho de que null
es identificado como de tipo objeto, hacen que tengamos que tener en cuenta este hecho antes de intentar tratarlo como un objeto. Estos casos se deben tener siempre presentes ya nos puede producir problemas o errores en nuestro código.
Nuestra función universal para comparar valores de cualquier tipo en Javascript va tomando forma, todavía es muy escasa su funcionalidad, pero nos está sirviendo como excusa para a dar un repaso a muchas características importantes e interesantes del lenguaje. 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.
EXCELENTE SERVICIO
Excelente aporte!! Tengo que entregar un trabajo práctico y me re sirvió, muchas gracias!
Excelente explicación! Gracias por compartir estos conocimientos.