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.