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

JSDayES – Vídeos

Si te lo has perdido o si quieres volver a ver las charlas y talleres del impresionante JSDayES 2017, aquí tienes todos los vídeos. Ponentes de primer orden, tanto nacionales como internacionales, muestran todos los aspectos de Javascript.

The CITGM Diaries by Myles Borins [video]

Myles Borins de Google, miembro del CTC de Node.js (Node.js Core Technical Committee), nos cuenta (en inglés, por supuesto) como funciona CITGM (Canary In The Gold Mine), una herramienta que permite obtener un módulo de NPM y probarlo usando una versión específica de NodeJS.

Debate: Tecnologías de Front Web [vídeo]

Desde las principales comunidades de desarrollo de tecnologías de front (Madrid JS, Polymer Madrid, Angular Madrid y VueJS Madrid) se ha organizado este debate que pretende ser un ejercicio de sentido común en relación a las tecnologías de front actuales centradas en componentes.

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.

Algunos 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.

Cómo diferenciar arrow function de function

En un reciente artículo Javier Vélez Reyes hace patente las principales diferencias entre las funciones tradicionales y las funciones flecha, ya que ambos modelos no son equivalentes e intercambiables. Veamos cómo es posible saber si una función ha sido construida por medio de la instrucción function o como una arrow function.