Seleccionar página

Javascript dispone de una serie de objetos que envuelven los tipos básicos o primitivos (String, Number y Boolean). Normalmente se diferencias los tipos primitivos y los objetos en que los primeros se escriben en minúsculas y los segundos son un constructor cuya primera letra se escribe en mayúscula. Además tenemos Date, un tipo de objeto muy común y del que no disponemos de un tipo primitivo como tal, ya que este objeto es la representación como fecha y hora de un valor numérico.

Esta nueva entrega de nuestra serie con motivo de la construcción de una función universal que compare cualquier tipo de datos en Javascript, vamos a dar un repaso a estos tipos de datos que se manejan como objetos y, por lo tanto, tenemos que tener en conocer sus características específicas para poder trabajar adecuadamente con ellos.

Trabajar con fechas

En Javascript hay realmente pocos tipos de datos nativos: numérico, cadena, lógico. Las fechas en Javascript son un objeto, no son un tipo primitivo, aunque son parte del lenguaje. Internamente contiene un valor numérico que representa los mili segundos transcurridos desde el 1 de enero de 1970 a las 0:00 UTC. Como consecuencia trabajar con fechas en Javascript no es algo evidente y tenemos que aprender cómo funciona, cómo operar con este tipo de datos, cómo convertirlas a otros tipos de datos y cómo compararlas.

Para operar con fechas (sumar, restar, etc.) debemos obtener el valor numérico interno de la fecha. Existen dos métodos para ello, getTime() y valueOf(). Ambos devuelven el mismo valor en milisegundos, pero el segundo método refleja con mayor claridad lo que estamos obteniendo y en nuestro caso es la que preferimos utilizar. getTime() puede confundirse con una función que devuelve la parte de hora (tiempo) de una fecha-hora, y no es así.

Si queremos obtener los milisegundos entre dos fechas, podemos hacer algo de este tipo:

var start = new Date(2017, 0, 01);
var now = new Date();
console.log(now.valueOf() - start.valueOf());

Comparar fechas

Para saber si dos fechas tienen el mismo valor, también tenemos que utilizar el valor numérico interno de la fecha. Como hemos comentado una fecha es realmente un objeto y, como seguramente sabremos, la comparación de objetos es bastante complicada, ya que === en el caso de los objetos devuelve true sólo si las dos expresiones que comparamos hacen referencia exactamente a la misma instancia del objeto, no si dos objetos tienen los mismos atributos o propiedades, sólo sin son dos referencias del mismo objeto. Por lo tanto, al comparar dos fechas con === obtendremos false, ya que no son el mismo objeto.

var date1 = new Date();
var date2 = new Date();
var date1b = date1;

console.assert( date1 !== date2 );
console.assert( date1 === date1b );

Para comparar fechas tenemos que utilizar getTime() o valueOf().

var date1 = new Date(2017, 0, 1);
var date2 = new Date(2017, 0, 1);
var date1b = date1;

console.assert( date1.getTime() === date2.getTime()  );
console.assert( date1.getTime() === date1b.getTime() );

console.assert( date1.valueOf() === date2.valueOf()  );
console.assert( date1.valueOf() === date1b.valueOf() );

Es importante recordar que para operar realmente con fechas debemos utilizar (casi siempre), el valor numérico de la fecha, es decir, el valor devuelto por valueOf().

Objetos creados con new y String(), new Number() y new Boolean()

Normalmente utilizaremos String(), Number() y Boolean() como funciones para convertir datos entre los diferentes valores, no obstante, también se pueden utilizar como constructores utilizando new, en cuyo caso devuelven un objeto que envuelve al tipo primitivo.

Aunque es común considerar que utilizar new con estas funciones es un error, lo cierto es que el lenguaje como tal lo acepta y no podemos ignorar esta circunstancia.

Igual que en el caso de las fechas, el problema es que los objetos devueltos por estos constructores no se pueden comparar con === con los valores básicos que contienen, ya que unos son objetos y los otros no. En este caso si podemos utilizar == que realizará una conversión implícita de tipos, pero como hemos visto, las conversiones implícitas son algo peligrosas y pueden producir efectos colaterales no deseados. Afortunadamente también podemos utilizar valueOf() que nos devolverá el tipo primitivo que está envuelto dentro del objeto.

var string1 = new String('Hello');
var string2 = "Hello";
console.assert(           string1 !== string2  );
console.assert(           string1 ==  string2  );
console.assert( string1.valueOf() === string2.valueOf()   );

var boolean1 = new Boolean(true);
var boolean2 = true;
console.assert(           boolean1 !== boolean2  );
console.assert(           boolean1 == boolean2   );
console.assert( boolean1.valueOf() === boolean2.valueOf()   );

var number1 = new Number(1);
var number2 = 1;
console.assert(           number1 !== number2  );
console.assert(           number1 == number2   );
console.assert( number1.valueOf() === number2.valueOf()   );

Aunque este puede ser un caso poco habitual y basta con no utilizar este tipo de objetos, lo cierto es que existen y en alguna ocasión podemos encontrarnos con ellos y deberíamos tener en cuenta cómo funcionan.

Incluir estas comparaciones en equal()

Siguiendo con nuestro desarrollo de una función universal que compare cualquier tipo de datos en Javascript, vamos a ver como incluir estaos casos en nuestra función de comparación. Para ello lo que vamos a hacer es:

  • Comprobar si los dos valores disponen de una función valueOf().
  • Comprobar si valueOf devuelve un valor diferente del propio valor original, es decir, que realmente tenemos un valor primitivo por debajo del objeto que lo contiene.
  • Comprobar si los dos valores devueltos por valueOf() son iguales o no
  • Para completar el proceso, comprobaremos si los dos objetos tienen igual constructor, y por lo tanto podemos considerar que son exactamente del mismo tipo o sólo tienen el mismo valor.

En el código hay que insertar algo de este tipo:

if (typeof a.valueOf === 'function' &&              // valueOf() is a function in both values
    typeof b.valueOf === 'function')
{
    aValue = a.valueOf();                           // Get valueOf()
    bValue = b.valueOf();
    if (aValue !== a || bValue !== b) {             // The valueOf's return is different that the base value
        if (aValue === bValue) {                    // The valueOf's return is the same for both values
            if (a.constructor === b.constructor) {  // It's the same constructor and as result is the same type
                return equal.VALUE_AND_TYPE;
            } else {
                return equal.VALUE;                 // Equal value (different type)
            }
        }
    }
}

Versión de esequal.js en GitHub y sus pruebas.

Conclusiones

Aunque los objetos que envuelven los tipos primitivos de Javascript son muy rara vez utilizados y realmente podríamos no tenerlos en cuenta, el tipo fecha es un dato muy importante y es ampliamente utilizado en nuestros programas, por lo que ser capaces de comprender su funcionamiento y cómo debemos trabajar con ellos sin problemas es clave para la solidez de nuestros desarrollos.

Nuestra función universal para comparar valores de cualquier tipo en Javascript va tomando forma. Ya es capaz de comparar fechas y no tiene problemas con los objetos que envuelven lo tipos primitivos. En los próximos capítulos avanzaremos comparando otros tipos de objetos analizando sus propiedades, 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)

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

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

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

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

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.