Las nuevas constantes en Javascript: explicación, ejemplos e inconsistencias

20 Jun 2014

Introducción

A decir verdad, pensaba que las nuevas constantes en Javascript no daban para escribir un artículo. Sin embargo, he ido comprobando que existen ciertas limitaciones o inconsistencias que cabría analizar para no cometer errores o caer en malos entendidos con esta funcionalidad. Como nota final, he añadido algunas de las constantes tradicionales que ya existen en el lenguaje además de aquellas que están por venir con ECMAScript 6.

Veamos qué podemos esperar a partir de ahora cuando declaremos valores con ‘const’ en Javascript…

Definiendo constantes

Para definir una constante, nos basta con declararla con la nueva palabra reservada ‘const’:

const FOO = 'Hello World';
 
console.info( FOO ); // Hello World
console.info( typeof FOO ); // string
console.info( ({}).toString.call( FOO ) ); // [object String]

Al igual que con las variables ordinarias, se pueden definir varias constantes de forma consecutiva separándo cada asignación con una coma:

const FOO = 'Hello World',
    BAR = 'Goodbye Lenin';

NOTA: Personalmente, no me gusta cómo queda ahora indentado el código: con ‘var’, al ser 3 letras, las siguientes líneas indentadas con 4 espacios dejaban los nombres de variables perfectamente alineados verticalmente. Las 5 letras de ‘const’ hacen que se pierda esa claridad.

Como convención, cabe destacar que solemos escribir las constantes en mayúsculas para distinguirlas del resto de variables; sin embargo, nada impide hacerlo en minúsculas. Ningún mensaje de advertencia nos recomienda el uso de mayúsculas:

const foo = 'Hello. I am in lowercase';

Como ocurre con el resto de variables, las constantes en Javascript pueden almacenar cualquier tipo de objeto:

const A_STRING = 'I am a string';
 
const A_NUMBER = 100;
 
const AN_ARRAY = [ 'var1', 12, { foo: 'bar' } ];
 
const AN_OBJECT = {
    name: 'MyAPP',
    ver: '1.0'
};
 
const A_MODULE = ( function () {
    var API = {
        foo: 'I am a property',
        bar: function () {
            console.info( 'I am a method' );
        }
    };
 
    return API
} () );

Y como cabe esperar, sus valores, una vez fijados, no deberían poder modificarse, ni ‘anularse’:

const FOO = 'Hello World';
 
FOO = 'Goodbye Lenin'; // TypeError: FOO is read-only
 
FOO = null; // TypeError: FOO is read-only

Con cadenas y números el comportamiento es el esperado, pero…

Incosistencias: objetos y arrays

Como comprobaremos a continuación, a día de hoy hay cierta inconsistencia cuando aplicamos métodos sobre una constante definida como array u objeto.

Veamos por ejemplo qué ocurre con los métodos de un array que modifican el valor original, o de la edición directa de los valores a través de índices:

const FOO = [ 'value1', 'value2', 'value3', 'value4' ];
 
FOO[ 1 ] = 'newValue';
 
console.info( FOO.shift() ); // value1
console.info( FOO.pop() ); // value2
 
FOO.length = 2;
 
console.info( FOO ); // [ "newValue", "value3" ]
 
console.info( FOO.join( '-' ) ); // newValue-value3

Como podemos ver, hemos hecho añicos la integridad del valor de FOO pese a estar declarado como constante y tener que ser por definición ‘intocable’.

El último ejemplo además, es interesante porque hemos modificado el tipo de una constante, pasándolo de array a cadena… Con esto vemos que, las constantes no son completamente intocables. Podemos modificar sus tipos y valores en tiempo de ejecución utilizando algunos de los métodos propios de cada uno de ellos.

Este tipo de alteraciones en los valores, afectaría también a las asignaciones mediante referencia. Por lo tanto, si una constante referencia a otro valor, si ese valor cambia, la constante lo hace también.

var myVarArr = [ 'val1', 'val2' ];
const myConstArr = myVarArr;
 
console.info( myVarArr, myConstArr ); // ["val1", "val2"] ["val1", "val2"]
 
myVarArr.pop();
console.info( myVarArr, myConstArr ); //  ["val1"] ["val1"]

Y tampoco nada nos impide el uso de ‘delete’ para borrar un valor determinado:

const myVarArr = [ 'val1', 'val2', 'val3' ];
 
delete myVarArr[ 1 ];
 
console.info( myVarArr ); // ["val1", undefined, "val3"]

Hemos visto más arriba el ejemplo con arrays pero con objetos obtenemos comportamientos igual de inconsistentes:

const FOO = { name: 'myApp', ver: '1.0' };
 
FOO.ver = '2.0';
 
console.info( FOO.ver ); // 2.0

Freeze al rescate

Para estos casos en los que necesitamos tanto arrays como objetos intocables, la forma más segura por el momento de proteger el valor de sus propiedades es mediante el método ‘freeze:

// In arrays
var arrModel = [ 'val1', 'val2' ];
 
var myArr = Object.freeze( arrModel );
 
myArr[ 0 ] = 'newVal'; // TypeError: 0 is read-only
// In Objects
var appModel = {
    name: 'myApp',
    version: '1.0'
};
 
var myApp = Object.freeze( appModel );
 
myApp.name = 'newApp'; // TypeError: "name" is read-only

¿Y qué hay del hoisting?

De igual forma que ocurre con las variables ordinarias (ya sean definidas con ‘var’ o con el nuevo ‘let’), las constantes continúan padeciendo el efecto del hoisting. Esto significan que están presentes en todo el scope en el que se declaren independientemente de dónde lo hagan explícitamente:

var myApp = function () {
    console.info( FOO );
 
    const FOO = 'Hello World';
 
    console.info( FOO );
};
 
console.info( myApp() );
// undefiend
// Hello World

Como podemos observar, el primer ‘console’ nos informa de que la constante está declarada, aunque sin el valor que le será asignado más adelante.

BONUS: Las viejas y nuevas constantes de Javascript (ES5 y ES6)

Las constantes en Javascript no son algo nuevo, pero hasta ahora, estaban reservadas al propio lenguaje, concretamente como propiedades del objeto Math. Algunas de las constantes ECMAScript 5 son:

// Constante de Euler
console.info( Math.E ); // ~2.718
 
// Logaritmo de 2
console.info( Math.LN2 );  // ~0.693
 
// Logaritmo de 10
console.info( Math.LN10 ); // ~2.302
 
// Logaritmo de E en base 2
console.info( Math.LOG2E ); // ~1.442
 
// Logaritmo de E en base 10
console.info( Math.LOG10E );  // ~0.434
 
// PI
console.info( Math.PI ); // ~3.14159
 
// Raíz cuadrada de 2
console.info( Math.SQRT2 ); // ~1.414
 
// Raíz cuadrada de 1/2
console.info( Math.SQRT1_2 );  // ~0.707

Con ECMAScript 6, se han añadido algunas constantes más al objeto Number pero dependientes ya de nuestra implementación concreta de Javascript:

// El menor intérvalo entre dos números que puede representarse
console.info( Number.EPSILON ); // ~2.220
 
// El mayor número positivo que puede representarse
console.info( Number.MAX_VALUE ); // ~1.797
 
// El menor número positivo que puede representarse (más cercano a cero)
console.info( Number.MIN_VALUE ); // ~5e-324

Conclusión

De nuevo, ES6 nos ofrece una funcionalidad que muchos desarrolladores han ido demandado con el paso de versiones: la declaración de constantes en Javascript. Sin embargo, parece que la implementación no es todo lo estricta que cabría esperar pues hemos comprobado cómo es posible alterar su valor (e incluso el tipo) cuando trabajamos con arrays u objetos. En esos casos, el método ‘freeze’ continúa siendo el más fiable para preservar esos valores.

Y esto es lo que dan de si por ahora las constantes, una nueva funcionalidad que, como parte de la especificación ECMAScript 6, es susceptible aún de sufrir alguna revisión.

Más:

{2} Comentarios.

  1. L. Ferreira

    Pues vaya! Que fail más grande de «const» xD. No se te escapa una joder!! 😀

    Además, aunque algo más enrevesado, con el patrón de módulo que tu has comentado en repetidas ocasiones en tu blog, se puede obtener un resultado similar, que (creo, si no se me escapa algo) permite la misma flexibilidad.

    var CONSTS = (function(){
    var contsValues = {
    jonh: true,
    alex: ‘Hi!’,
    mike: function(){
    return ‘yeah!’;
    }
    };
    return {
    get: function(param){
    return contsValues[param];
    }
    }
    })();

    console.log(CONSTS.get(‘alex’));
    console.log(CONSTS.get(‘mike’)());

  2. Aenun Nadifah

    Gracias una guía que da una muy completa, y voy a tratar de aprender

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *