En este artículo te mostramos el Top de 10 errores principales en JavaScript, qué los causa y como evitarlos. Si logras evitar estos errores, te convertirás en un desarrollador más eficaz.

Aqui nos centraremos en los 10 errores que con mayor probabilidad suelen aparecer. Este estudio es de RollBar, y está hecho sobre una muestra de más de 1000 proyectos JavaScript.

Los 10 errores más comunes en JavaScript

Aquí tienes la gráfica de los errores más comunes que han aparecido en el muestreo de más de 1000 proyectos. Analizaremos cada uno de estos errores para evitarlos en nuestros futuros proyectos.

Top 10 errores JavaScript

1 - Uncaught TypeError : cannot read property

Si eres un desarrollador JavaScript, lo más probable es que hayas visto este error más de lo que quisieras admitir. Este error ocurre en Chrome cuando lee una propiedad o llama a un método de un objeto indefinido.

Puedes probar esto muy fácilmente con la consola de Chrome:

captura1

Esto puede ocurrir por varias razones, pero lo común es por culpa de la inicialización incorrecta del estado al representar los componentes de la interfaz de usuario.

Veamos un ejemplo en React, pero los mismos principios de inicialización incorrecta también aplican para Angular, Vue o cualquier otro framework:

class Quiz extends Component {
      componentWillMount() {
    axios.get('/thedata').then(res => {
      this.setState({items: res.data});
    });
  }
  render() {
    return (
      <ul>
        {this.state.items.map(item =>
          <li key={item.id}>{item.name}</li>
        )}
      </ul>
    );
      }
   }

`

Hay dos cosas importantes que pasan aquí:

  1. El estado de un componente (ejemplo: this.state) comienza como indefinido.
  2. Cuando recuperas los datos de manera asíncrona, el componente se renderizará al menos una vez antes de que se carguen los datos, independiente de si se han obtenido en el constructor, componentWillMount o componentDidMount. Cuando en Quiz renderiza por primera vez, this.state.items es indefinido (undefined). Esto, a su vez, significa que ItemList obtiene los elementos como indefinidos y se obtiene el error: “Uncaught TypeError: cannot read property ‘map’ of undefined” en la consola.

Este error es fácil de arreglar de la siguiente manera: Inicializa el estado con valores por defecto razonables en el constructor.

class Quiz extends Component {
  // Added this:
      constructor(props) {
    super(props);
// Assign state itself, and a default value for items
    this.state = {
      items: []
    };
   }
  componentWillMount() {
    axios.get('/thedata').then(res => {
    this.setState({items: res.data});
    });
   }
  render() {
    return (
      <ul>
        {this.state.items.map(item =>
          <li key={item.id}>{item.name}</li>
        )}
      </ul>
    );
  }
}

NOTA

El código exacto en tu aplicación puede ser diferente, pero esperamos que este ejemplo sirva para que puedas corregir este tipo de error y evitarlo en tu app.

2 - TypeError: ‘undefined’ is not an object (evaluating)

Este es un error que ocurre en Safari cuando lees una propiedad o llamas a un método en un objeto indefinido. Puedes probar esto fácilmente en la consola de Safari. Este es prácticamente el mismo error que el anterior para Chrome, pero para Safari usa un mensaje de error diferente.

Habría que tomar las mismas soluciones.

3 - TypeError: null is not an object (evaluating)

Este es un error que aparece en Safari cuando lees una propiedad o llama a un método de un objeto nulo. Puedes probar esto en la consola de Safari.

Recuerda que en JavaScript, null o undefined no son lo mismo, por lo que se ven dos mensajes de error diferentes.

Undefined suele ser una variable que se ha asignado, mientras que Null significa que el valor esta en blanco. Para verificar que no son iguales, hay que usar el operador de igualdad estricta para verificarlo:

  • Este error puede aparecer si se intenta usar un elemento DOM en JavaScript antes de que se cargue el elemento. Esto se debe a que la API DOM devuelve null.

  • Cualquier código JavaScript con elementos en el DOM debería ejecutarse después de que se hayan creado los elementos en el DOM.

En JavaScript el código se interpreta de arriba a abajo como se describe en el HTML. Entonces si hay una etiqueta antes de que cargue los elementos del DOM, por tanto arrojaria dicho error si el orden no es el correcto.

En este ejemplo, podemos resolver este problema añadiendo un evento listener que nos notificará cuando la pagina esté lista. Una vez que ddEventListener se activa, el método init() puede hacer uso de los elementos del DOM.

<script>
      function init() {
    var myButton = document.getElementById("myButton");
    var myTextfield = document.getElementById("myTextfield");
    myButton.onclick = function() {
          var userName = myTextfield.value;
        }
      }
      document.addEventListener('readystatechange', function() {
    if (document.readyState === "complete") {
          init();
        }
      });
</script>
<form>
      <input type="text" id="myTextfield" placeholder="Type your name" />
      <input type="button" id="myButton" value="Go" />
</form>

4 - Unknown: Script error

Este error nos avisa cuando se origina un error de un archivo JavaScript servido desde un origen diferente (dominio, puerto o protocolo diferente).

Es un error que no sabemos de donde viene ya que, no se sabe cuál es el error, ni de parte del código se está originando este error.

Por ejemplo, si nuestro código está en un CDN, cualquier error no capturado se informará simplemente como Script error, en lugar de contener información útil.

Esta es una medida de seguridad del navegador destinada a evitar el paso de datos a través de dominios que de otro modo no podrían comunicarse.

Para obtener los mensajes de error con información útil, debes hacer lo siguiente:

  1. Añadir una cabecera Cross Origin Resource Sharing (CORS), configurando la cabecera access-control-allow-origin en tú codigo(*) significa que se puede acceder al recurso correctamente desde cualquier dominio. Puedes reemplazar el * con tu dominio de acceso, por ejemplo:

    Ejemplo: access-control-allow-origin: www.ejemplo.com.

    Sin embargo, manejar diferentes dominios es complicado y puede que no valga la pena si usas un CDN debido a problemas de almacenamiento en caché que puedan surgir. Puedes ver más información sobre este tema en el hilo de Stack Overflow aquí

    Vamos a ver algunos ejemplos sobre como configurar este encabezado en varios entornos:

    Apache:
    Crear o modifica tu archivo .htaccess con la siguiente expresión:

     Header add Access-Control-Allow-Origin "*"
    

    Nginx:
    Añadir la directiva add_header:

     location ~ ^/assets/ {
         add_header Access-Control-Allow-Origin *;
     }
    

    HAProxy:
    Agregua la dirfectiva siguiente a tu backend:

      rspadd Access-Control-Allow-Origin:\ *
    
  2. Agregar un encabezado HTTP Cross Origin en tu fuente HTML, para cada uno de los scripts para los que se ha establecido el encabezado access-control-allow-origin, debes establecer crossorigin="anonymous" en la etiqueta script.

    Asegurate de verificar el envío antes de agregar el encabezado crossorigin="anonymous". En Firefox, si el atributo está presente pero el encabezado no lo está, el script NO se ejecutara.

5 - TypeError: Object doesn’t support property

Este es un error que ocurre en Internet Explorer cuando haces uso de un método indefinido. Puedes consultarlo en la consola de IE:

captura2

Este es un equivalente al error “TypeError: ‘undefined’ no es una función” en Chrome. Si, diferentes navegadores pueden tener diferentes mensajes por el mismo error.

Este es un problema común para Internet Explorer en aplicaciones web que emplean namespace. Cuando este es el caso, el 99.9% del tiempo es la incapacidad de IE de vincular métodos dentro del namespace actual con la palabra clave this.

Por ejemplo: Si tienes un namespace llamado Rollbar con el método isAwesome(),si este se encuentra dentro de namespace Rollbar puedes invocar dicho método con la siguiente sintaxis:

this.isAwesome();

Chrome, Firefox y Opera aceptará sin ningún problema esta sintaxis. IE, por otro lado, no lo hará. Por lo tanto, la apuesta más segura es utilizar el namespace JavaScript siempre como prefijo para llamar al metodo, asi:

Rollbar.isAwesome();

6 - TypeError: ‘undefined’ is not a function

Este es un error que ocurre en Chrome cuando llamas a una función no definida. Puedes probar esto en la consola de Chrome y Firefox:

captura3

Como las técnicas de codificación y diseño de JavaScript se han convertido más sofisticadas a lo largo de los años, ha habido un aumento correspondiente a la proliferación de ámbitos de autorreferencia dentro de las devoluciones de callbacks y closures, que son una fuente bastante común de esta confusión.

Veamos como ejemplo, el siguiente fragmento de código:

function clearBoard(){
      alert("Cleared");
}
document.addEventListener("click", function(){
      this.clearBoard(); // what is “this” ?
});

Si estas ejecutando el código anterior y luego haces click en la página, se produce el siguiente error: “Uncaught TypeError: this.clearBoard is not a function”.

La razón es porque la función anónima que se está ejecutando se encuentra en el contexto del documento, mientras que clearBoard se define en el objeto Window

Para solventarlo podemos hacer una solución que se utilizaba antes en antiguos navegadores, simplemente guardar la referencia this en una variable que luego puede ser heredada..

Ejemplo:

var self=this;  // save reference to 'this', while it's still this!
document.addEventListener("click", function(){
      self.clearBoard();
});

Otra alternativa solo para navegadores más recientes, es que puedes usar el método bind() para pasar la referencia adecuada:

document.addEventListener("click",this.clearBoard.bind(this));

7- Uncaught RangeError: Maximum call stack

Otro error que sólo ocurre en Chrome en dos ocasiones. Una es cuando llamamos a una función recursiva que no tiene fin.

captura4

La otra es cuando pasas un valor a una función que esta fuera del rango establecido. Muchas funciones solo aceptan un rango especifico de números para sus valores de entrada.

Por ejemplo:

**Numer.toExponential(digitos) y Number.toFixed(digitos) tiene un rango de dígitos del 0 al 20, y Number.toPrecision(digitos) acepta los números del 1 al 21.

var a = new Array(4294967295);  //OK
var b = new Array(-1); //range error

var num = 2.555555;
document.writeln(num.toExponential(4));  //OK
document.writeln(num.toExponential(-2)); //range error!

num = 2.9999;
document.writeln(num.toFixed(2));   //OK
document.writeln(num.toFixed(25));  //range error!

num = 2.3456;
document.writeln(num.toPrecision(1));   //OK
document.writeln(num.toPrecision(22));  //range error!

8 - TypeError: Cannot read property ‘length’

Esto es un error que ocurre en Chrome debido a la lectura de la propiedad length para una variable indefinida. Podemos ver el fallo en la consola:

captura5

Normalmente length está definido en un array, pero es posible que se encuentre este error si el array no se inicializa o si el nombre de la variable está oculto en otro contexto. Veamoslo con un ejemplo:

var testArray= ["Test"];
function testFunction(testArray) {
    for (var i = 0; i < testArray.length; i++) {
          console.log(testArray[i]);
    }
}
testFunction();

Cuando declaras una función con parámetros, estos parámetros son locales. Esto significa que incluso si tienes variables con nombres testArray, los parámetros con los mismos nombres dentro de una función se trataran como locales.

Hay dos maneras de resolver este problema:

  1. Elimina todos los parámetros en la declaración de la función:
var testArray = ["Test"];
/* Precondición: definir testArray fuera de la función */
function testFunction(/* Sin parámetros */) {
    for (var i = 0; i < testArray.length; i++) {
     console.log(testArray[i]);
       }
}
testFunction();
  1. Llama a la función pasando el array que declaramos:

    var testArray = [“Test”];

    function testFunction(testArray) {

       for (var i = 0; i < testArray.length; i++) {
      console.log(testArray[i]);
        }
     }
     testFunction(testArray);
    

9 - Uncaught TypeError: Cannot set property

Cuando tratamos de acceder a una variable no definida siempre retorna undefined y claro, no podemos obtener ni enviar ninguna propiedad de undefined. En este caso, nos dará el error **“Uncaught TypeError cannot set property of undefined.”

Replicamos el error en Chrome:

captura6

Como test tiene un valor inicial de Undefined, la consoloa nos avisará del error Uncaught TypeError cannot set property of undefined.

10 - ReferenceError: event is not defined

Y para finalizar con el top 10 de los errores más usados, vemos que este error lo encontramos cuando tratamos de acceder a una variable que no está definida o no podemos acceder a ella por que está fuera del ámbito establecido. Replicamos el error en la consola de Chrome:

captura7

Las librerias como jQuery intentan normalizar este comportamiento. Sin embargo, es una buena práctica crear este evento y usarlo:

document.addEventListener("mousemove", function (event) {
      console.log(event);
})

Conclusión

Como puedes ver, la mayoria de los errores aparecen por hacer uso denulos o indefinidos. Haciendo uso de Typescript y con la compilación estricta podrías evitarlos facilemente.

Esperamos que con este listado de los errores más comunes en JavaScipt puedas evitarlos en un futuro y ser más eficiente. Aunque nunca evitaremos todos los errores en nuestro código, para ello es importante tener la capacidad de ver errores y resolverlos.

Fuente: Top 10 JavaScript Error from 1000+ projects