lunes, 7 de enero de 2013

Lo peor de JavaScript

El libro de Douglas Crockford "JavaScript, the good parts" incluye un apéndice con las que son, a su juicio, las peores partes del lenguaje. Este es un resumen de esos puntos negros que conviene conocer y evitar (algunos se han solucionado en ECMAScript 5).

Variables globales

Las variables globales son variables visibles en cualquier contexto y el problema más grave de JavaScript es su dependencia de ellas. Al no existir un compilador, la única manera de enlazar unos scripts con otros es mediate el uso del contexto global.

Este tipo de variables pueden ser modificadas por cualquier script en cualquier momento, incluyendo una librería externa que puede utilizar el mismo nombre de variable que nosotros para otro propósito.

Para agravar el problema, cualquier variable que se declare sin var, en cualquier contexto, se convierte automáticamente en global.

myVar = "hello";   //myVar es una variable global

Contexto

El contexto (scope) de una variable en JavaScript es la función en la que está declarada (o el global si no está dentro de ninguna).



function test() {
  var one = 1;
  
  // var one is visible inside the function

}

// var one is not visible here

En la gran mayoría de los lenguages de programación, las variables tienen visibilidad sólo dentro del bloque en el que se crean ( un bloque se delimita entre llaves ). La sintaxis de JavaScript permite definir bloques, pero no proporciona un contexto de bloque para sus variables.


if ( valid ) {
    var myVar = "this is valid";
    …
    //esto es un bloque
    //myVar está declarada en este bloque
    //pero será visible fuera de él
    //No tiene un contexto de bloque

}  
  // myVar es también visible aqui

Inserción automática de punto y coma

El punto y coma al final de una sentencia no es necesario en JavaScript, el intérprete lo inserta automáticamente. Esto parece cómodo pero en realidad puede dar lugar a errores muy difíciles de encontrar al colocar el ';' en lugares incorrectos.

Por ejemplo, si queremos devolver un objeto en una función y lo escribimos así:


…
…
return
{
    result: true
};


En realidad estaremos devolviendo undefined porque el punto y coma se inserta justo detras de return. No habrá warning ni error. Para evitar estos problemas es recomendable incluir siempre los ';' cuando escribimos el código.

Palabras reservadas

JavaScript tiene más de 60 palabras reservadas de las que la mayoría no se utilizan realmente en el lenguaje. El problema es que no pueden utilizarse como nombres de variables, parámetros, objetos o funciones.

Si queremos utilizarlas como propiedades de un objeto, deben ir entre comillas y no puede utilizarse la notación '.' para referenciarlas:


var myObject = { 'final': true}; //final es una palabra reservada
myObject.final = false;          // Error
myobject['final'] = false;       // Correcto

Unicode

La implementación de Unicode en JavaScript es obsoleta. Se diseñó para representar un total de 65.563 caracteres pero actualmente Unicode soporta más de 1 millón.

El problema es que JavaScript representa siempre los caracteres con 16 bits. Cuando se necesitan más bits para representarlo ( por encima de 65.563 ) JavaScript lo considera como 2 caracteres distintos, cada uno con 16 bits.

typeof

El operador typeof tiene graves fallos que nos obligan a manejarlo con mucho cuidado. Por ejemplo, si hacemos:

typeof null;   //returns "object"

Nos devolverá "object". Un resultado que no es lógico ni es de mucha ayuda. El operador también devuelve "object" para un array.

Para expresiones regulares como:

typeof /a/;  

El resultado varía. En algunas implementaciones se devuelve "object" y en otras "function".

parseInt

la función parseInt(string, radix) se utiliza para transformar un string en un entero. El parámetro opcional radix indica la base en la que está representado el entero.

Cuando se utiliza la función sin indicar radix es facil que se produzcan errores porque cualquier string con un "0" delante se evalua como un entero en base 8.

parseInt("09"); // 0 porque el 9 no existe en base 8
parseInt("046"); // 38, es 46 en base 8 
parseInt("046", 10); // 46. Indicamos explicitamente que trabajamos en decimal

Para evitar estos errores es recomendable indicar siempre el radix. ECMAScript 5 corrige este problema considerando siempre que la base es decimal por defecto.

Operador +

El operador + está sobrecargado en JavaScript y puede sumar números o concatenar strings, dependiendo únicamente del tipo de los parámetros.

  • Si cualquiera de los operandos es un string vacio, devuelve el otro operando convertido en un string.
  • Si los dos operandos son números devuelve la suma.
  • En cualquier otro caso convierte ambos operandos en strings y los concatena.

Este comportamiento es una fuente de errores.

Números en coma flotante

JavaScript implementa el estándar IEEE 754 ( Binary Floating-point Arithmetic ) que no es adecuado para el manejo de fracciones. Produce pequeños errores debidos al redondeo.

Una forma de evitar los errortes es intentar trabajar siempre con enteros con un factor de escalado.

NaN

NaN significa "Not a Number" y es un valor definido en el estandar IEEE 754 del que hablamos antes.

Lo curioso de NaN son los siguientes comportamientos:

    typeof NaN === 'number';     //true
    NaN === NaN;                 //false
    NaN !== NaN;                 //true
    

Es decir, typeof NaN nos indica que es un número pese a que representa exactamente lo contrario. Ademas, NaN no es igual a si mismo.

Arrays

JavaScript no tiene arrays reales, con memoria consecutiva reservada para los elementos. Los implementa como objetos cuyos índices son enteros. Esto tiene dos consecuencias negativas:

  • El rendimiento es bastante peor que con arrays reales
  • El operador typeof no distingue entre objetos y arrays

Valores false

Los siguientes valores son false en JavaScript:

  • 0
  • NaN
  • ""
  • false
  • null
  • undefined

La confusa diferencia entre null y undefined puede llevar a error. Lo más grave es que undefined y NaN no son constantes, son variables globales y su valor puede modificarse causando errores en el resto de los scripts de la página.

hasOwnProperty

Este método puede utilizarse para filtrar, en un bucle for in, las propiedades de un objeto que vienen heredadas de su prototipo. El problema es precisamente que es un método y no un operador. Cualquier objeto puede reemplazarlo por su propia versión o por cualquier valor, haciendolo poco fiable.

Object

El problema en este caso es que los objetos en JavaScript nunca están realmente vacios. Heredan las propiedades de la cadena de prototipos. Si no tenemos esto siempre en cuenta podemos incurrir en errores provocados por propiedades/métodos con los que no contamos.

No hay comentarios:

Publicar un comentario