Punto y coma en Javascript… ¿Es realmente necesario?

¡Saludos visitante! si deseas comentar o hacer una pregunta sobre este post por favor dirígete a la nueva dirección en http://variabletecnica.com.ve. La página que estás leyendo dejará de estar disponible el 15/11/2015. Gracias, y disculpa las molestias 🙂 Cuando empecé a programar en Javascript lo hice usando ejemplos que iba encontrando a medida que aprendía.…


¡Saludos visitante! si deseas comentar o hacer una pregunta sobre este post por favor dirígete a la nueva dirección en http://variabletecnica.com.ve. La página que estás leyendo dejará de estar disponible el 15/11/2015. Gracias, y disculpa las molestias 🙂

Cuando empecé a programar en Javascript lo hice usando ejemplos que iba encontrando a medida que aprendía. Con el tiempo me di cuenta de que algunas veces en los ejemplos terminaban las instrucciones con punto y coma «;«, pero como vi que no era necesario para que el código funcionara simplemente nunca lo utilizaba.

Un buen día decidí probar un minimizador de Javascript, y me llevé la sorpresa de perder varias horas agregando «;» al final de cada instrucción para hacerlo funcionar. Y no es que el minimizador tuviese un bug; la razón de que fallara es que los interpretes de Javascript (como el navegador web) leen el código línea por línea hasta que encuentran un punto y coma, o alguna pista que les indique que llegaron al final de la instrucción, pero al minimizar el código la principal «pista» (el fin de línea) desaparece. Veamos un ejemplo de estas «pistas»:

var numero = 3
var cadena = 'Valor secreto: '
alert(cadena + numero.toString())
numero = numero + 1

El interprete Javascript se encuentra con la declaración de la variable numero, como lo siguiente es un signo = supone que a la variable que se acaba de declarar se le asignará el siguiente valor (3). Al número 3 le sigue un terminador de línea (los espacios y tabulaciones adicionales son irrelevantes) seguido de una nueva declaración de variable, por lo que puede dar por terminada la instrucción anterior.

El mismo proceso se repite para la segunda instrucción: luego de la cadena 'Valor secreto: ' se encuentra con un terminador de línea seguido de una llamada a la función alert, por lo que da por terminada la segunda instrucción y pasa a la siguiente.

En este proceso, el interprete «inserta» los «;» cuando supone que termina cada instrucción… así es, si no colocas los «;» donde van, el interprete tendrá que adivinar donde colocarlos. Si lo piensas por un momento verás que no es necesario minimizar el código para encontrarnos con errores, basta con olvidar colocar «;» en un punto obligatorio o donde se presente una ambigüedad.

Veamos unos ejemplos de estos casos, tomados de la Guía de Estilos de Google.

Ejemplo 1: Punto y coma luego de una expresión de función

Una Expresión de Función es, bueno, exactamente eso: una expresión que devuelve como valor una función; en el siguiente ejemplo, la primera función es una expresión de función en una asignación y debe terminar en punto y coma; la segunda función es también una expresión de función, que esta encerrada entre paréntesis y además está seguida por un par de paréntesis, lo que la convierte en una función autoejecutable, y también debe terminar en punto y coma. La tercera es una declaración de función, en este caso nunca hay ambigüedad y el interprete siempre reconocerá el inicio y fin de la instrucción, aunque igual podemos colocar el «;» al final, ya que no está prohibido tampoco.

var miMetodo = function() {
  return 42;
};

( function(lcMensaje){
  alert(lcMensaje)
})('Hola!');

function sumarNumeros(a, b){
  return a+b;
}

El siguiente es un ejemplo de porqué es necesario terminar cada expresión de función con «;«.

//Error: una expresión de función debe terminar con ";"
var miMetodo = function() {
  return 42;
}  //Olvidamos el punto y coma que va aquí.

(function() {
  //Aquí tenemos algún código de inicialización, 
  //dentro de una función autoejecutable 
  alert('Autoejecutada ahora!')
})(); //Este punto y coma es necesario, 
      //pero para este ejemplo no importa

En el fragmento de código anterior, esperaríamos que la primera función sea asignada a una variable, y luego se ejecute la función autoejecutable (mostrando el mensaje 'Autoejecutada ahora!'). Si ejecutamos el código en una consola (yo lo hice en Google Chrome) veremos un error como Uncaught TypeError: number is not a function.

Veamos el problema: una expresión de función termina con una llave de cierre «}«, pero si el siguiente carácter (además de los terminadores de línea que mencioné antes) es un paréntesis de apertura «(«, el intérprete supondrá que es una función autoejecutable, y le pasará como parámetro lo que encuentre entre los paréntesis.

Así, el ejemplo anterior, si olvidamos colocar el «;«, es equivalente a esto:

var miMetodo = function() {
  return 42;
}(function(){ alert('Autoejecutada ahora!') })();

Esto es: la función autoejecutable es pasada como parámetro (sin ser ejecutada: el alert no se muestra) a la primera función, ésta se ejecuta devolviendo el valor 42, pero aun queda un par de paréntesis al final, o sea 42(), y eso explica el error number is not a function, porque 42 no es una función.

Si agregamos el punto y coma donde va se corrige el error.

Ejemplo 2: Punto y coma luego de notación de objeto

En Javascript, la notación de objeto (similar a la notación JSON) puede ser usada en cualquier lugar donde se pueda usar una variable de objeto, pero si estás definiendo un objeto para asignarlo a una variable, entonces debe terminar con punto y coma. Veamos:

//Una variable que recibe '0' si es un nuevo usuario, 
//o '1' si está registrado. 
var estaRegistrado = 0;
//Un par de funciones, terminadas en ";" como es debido.
var usuarioNuevo = function(){alert('Debes registrarte.')};
var usuarioRegistrado = function(){alert('¡Bienvenido!')};
//Una variable cualquiera, con un nuevo objeto
var x = {
  'i': 1,
  'j': 2
}  // Ups! olvidamos el punto y coma.

// La siguiente línea es rebuscada, pero técnicamente 
// válida y legal. Podría reescribirse con un 'if' 
// para mayor claridad
[usuarioNuevo, usuarioRegistrado][estaRegistrado]();

Una explicación sobre la última instrucción: la intensión del programador aquí es que si el usuario no está registrado (estaRegistrado=0) se ejecutará la función usuarioNuevo(), pero si está registrado se ejecutará la función usuarioRegistrado(). Esta instrucción debería estar programada usando un if, pero la dejaremos así para nuestro ejemplo.

Si ejecutas esto en una consola verás un mensaje como TypeError: Cannot call method '0' of undefined (éste es el mensaje que muestra en Google Chrome, puede ser diferente en otra consola Javascript).

El error tiene una explicación lógica, aunque un poco complicada; todo funciona normalmente hasta que definimos la variable x, luego, en la definición de la variable intentamos asignarle un literal de objeto pero justo después del él, el siguiente carácter que se encuentra es el corchete de apertura «[» de un literal de array. El motor Javascript supone que el corchete es para leer una propiedad del objeto (como x['miPropiedad']), así que toma el valor de la primera variable del array (usuarioNuevo), lo convierte en string y busca una variable con ese nombre en el objeto que acabamos de crear, pero como no existe obtenemos undefined; lo mismo ocurre con el segundo corchete: toma el valor de la variable estaRegistrado, lo convierte en string y, como le sigue un par de parentesis, trata de leer un método con ese nombre en el valor undefined, y como ya sabemos el valor undefined no tiene un método llamado ‘0’.

Para simplificar un poco, el código anterior es equivalente a este:

var x=({'i':1,'j': 2})["function(){alert('Debes registrarte.')}"]["0"]()

Fíjate que el contenido de la función usuarioNuevo y la variable estaRegistrado están entre comillas: los valores son convertidos en cadenas antes de buscar una propiedad con ese nombre.

Ejemplo 3: Punto y coma luego de un literal de arreglo

Un Literal de Arreglo es una lista de elementos encerrada entre corchetes («[» y «]«) y puede ser utilizado en cualquier lugar donde se pueda usar una variable que contenga un Array, pero para evitar ambigüedad si se usa en una asignación la expresión debe terminar con «;«. Por otro lado, Las operaciones matemáticas no están definidas para los arreglos, así que si intentas sumar un arreglo y un número ambos operandos se convertirán en cadenas y se concatenarán; si intentas cualquier otra operación matemática, como una resta, el resultado será NaN. Nuevamente, veremos el ejemplo y luego la explicación del problema:

//Definimos un par de funciones, con su ";" obligatorio
var procesar = function(){
  console.log('Procesando...'); 
  return -1
};
var terminar = function(){
  console.log('Terminando...')
};
//Definimos un arreglo
var postres = ['Pera', 'Pudín', 'Helado']//Faltó ";" aquí!
//Una inocente Ejecución Condicional "a la bash"
-1 == procesar() || terminar();

La intensión del programador en la última instrucción es que: si la función proceso devuelve -1 entonces la función terminar no se ejecutará (el operador lógico OR o «||» en Javascript no evalúa la segunda parte si la primera devuelve True).

Si ejecutamos esto en la consola no veremos ningún mensaje de error, pero la ejecución no será la esperada: en el ejemplo la función procesar devuelve -1 por lo que la función terminar no debería ejecutarse, pero en la consola veremos el mensaje 'Terminando...', así que efectivamente si se ejecutó. Más aun, la variable postres no contiene el arreglo, sino el valor undefined.

Analicemos lo que pasó: La definición de las funciones procesar y terminar se ejecuta correctamente (ambas terminan con el «;» correspondiente). Luego se define la variable postres y se le intenta asignar un arreglo, pero la instrucción no ha terminado! después del arreglo hay un signo de resta «-» seguido de un 1, así que al arreglo se le resta 1 dando como resultado NaN (por ser una operación matemática no permitida). Luego viene la comparación de igualdad ==, pero en este caso ya no importa el resultado de operacion (que se ejecuta correctamente) porque nada es igual a NaN, ni siquiera NaN. como esta comparación siempre devuelve False, siempre se ejecuta la segunda parte del OR, la función terminar, cuyo resultado es lo que finalmente es asignado a la variable postres, en este caso undefined; así es, todo esto fue solo una instrucción.

Una vez más, con solo agregar el punto y coma donde corresponde, la ejecución del código es la esperada.

Conclusión

En pocas palabras: si, el punto y coma en Javascript es necesario. Los interpretes Javascript, como los navegadores, son capaces de corregir en gran medida nuestro código e insertar los punto y coma donde van, pero la heurística para hacerlo (detallada en la sección 7.9 del estándar ECMA-262, ver referencias al final) es más o menos esto: todo es parte de la misma instrucción, hasta el siguiente punto y coma «;», hasta la siguiente llave de cierre «}», o hasta un terminador de línea seguido de un carácter que no pueda formar parte de la instrucción actual. En resumen, no insertará un «;» a menos que el no hacerlo produzca un error de sintaxis en la instrucción actual.

Se considera una buena práctica de programación en Javascript siempre terminar tus instrucciones con punto y coma, en lugar de aprender complejas reglas de cuando es necesario y cuando opcional. Puedes hacer una búsqueda rápida en Internet, y comprobar que la mayoría de los programadores lo recomiendan.

Referencias

Cita del día

I love the semicolon; it’s unnecessary, but graceful and sophisticated.
Brian P. Cleary


2 respuestas a “Punto y coma en Javascript… ¿Es realmente necesario?”

  1. Como se describe esta técnica ????
    [usuarioNuevo, usuarioRegistrado][estaRegistrado]()
    Donde podría aprender más ?

Soluciones claras y simples



Ing. Industrial, dedicado a la programación en ASP.NET+VB, SQL y Javascript+AJAX; y un poco de Android, Kotling, y Unity 🙂
Valencia, Venezuela



¿QUIERES APOYARME?

¿Te ha sido de ayuda alguno de mis artículos? Generar contenido técnico requiere de tiempo y esfuerzo. Con tu colaboración me puedes ayudar a mantener mi blog activo y actualizado. Si quieres y puedes apoyarme has clic aquí:

https://paypal.me/roimergarcia


ENTRADAS RECIENTES