Modas en JS: flechas, punto y coma y const, una vez más.

Si usas siempre flechas para definir tus funciones, usas const para declararlas como variables y/o no pones puntos y coma para separar tus sentencias, lo siento, pero lo estás haciendo mal.

El otro día, mi amigo y compañero Jaime, que está siguiendo un curso de nodeJS, me envió un correo pidiéndome opinión. Su profe en el curso les puso un código parecido a este como ejemplo.

const mult = (a, b) => a * b
const factorial = a => {
	if(a <=1) {
		return 1
	}
	return mult(a, factorial(a-1))
}

factorial(5)

Lo he modificado ligeramente para facilitarme la línea argumental con la que pretendo convencerte de que este código es terrible y, si es posible, de que convenzas a otros de no seguir esta tendencia de no usar puntos y coma para finalizar las sentencias, utilizar const para todo y definir todas las funciones con flechas.

Antes de nada, así es como escribiría yo este programa:

factorial(5);

function factorial(a) {
   if (a <= 1) {
       return 1;
   }   
   return mult(a, factorial(a-1));
}

function mult (a,b) {
    return a * b;
}

Y vamos a suponer que estamos hablando de un módulo que calcula algo más complejo, que usa más funciones y sigue un algoritmo más enrevesado.

Tomemos este código como un ejemplo simplificado sobre el que es más fácil pensar en lo que son buenas costumbres y lo que no lo son.

¿Cual de las dos versiones piensas que es mejor, más legible, menos confusa, menos propensa a errores?

¿Por qué?

Sea cual sea tu respuesta a la primera pregunta, deberías tener un conjunto de reglas sencillas que te permitan contestar este tipo de preguntas y argumentar el porqué. Estas son las mías:

  1. Escribe código para seres humanos, no para máquinas (Si no estás de acuerdo con esto aprende ensamblador, no un lenguaje de alto nivel)
  2. Escribe código claro, coherente y conciso, en ese orden de preferencia. Esto es consecuencia directa y complementa la regla 1.
  3. Evita en lo posible patrones y costumbres que puedan producir bugs difíciles de encontrar y/o resolver (aunque solo sea en casos marginales).
  4. Si tienes un buena excusa, sáltate las reglas anteriores. ¡Pero tiene que ser realmente buena!

Ahora, los que defienden el código que el profe puso como ejemplo suelen argumentar que "a menos caracteres más legibilidad", y en ese afán por arañar caracteres es perfectamente válido comerse hasta los puntos y coma.

No escribas sin signos de puntuación

Quitemos los puntos y coma de en medio lo primero… 😉 Pun intended!.

Si escribes sin puntos y coma estás haciendo que tu transpilador o el propio motor de Javascript los inserte automáticamente por ti.

Y no es que los motores tengan bugs al respecto, es que la especificación está mal y obliga a motores y transpiladores a insertar puntos y coma donde no deben. Se sabe desde hace años y no tiene remedio, porque cambiar la especificación ahora implica romper miles de páginas web que la gente usa a diario. Este es uno de los pilares de la especificación, nos guste o no: no romper la web.

Un bug en tu código que tenga que ver con esto es fácil de encontrar si pones habitualmente tus puntos y coma al final de cada sentencia, pero accidentalmente olvidas alguno.

Sin embargo, es virtualmente imposible de encontrar si no usas puntos y coma en absoluto.

Como muestra un botón:

const a = 1
const b = 2
const c = a + b
(a + b).toString()

Produce la excepción:

TypeError: b is not a function 

Lo cual suena absurdo. ¿Cuanto tiempo crees que vas a perder intentando averiguar que rayos está pasando, si tu empeño es evitar los puntos y coma?

Hasta que lees la especificación y te das cuenta de que Javascript autoinserta puntos y coma para producir este código:

const a = 1;
const b = 2;
const c = a + b(a + b).toString();

Otro botón:

const dameUnColor = () => {
    return
    {
        color: 'white'
    }
}
console.log(dameUnColor().color) // TypeError
Uncaught TypeError: Cannot read property 'color' of undefined

En este caso las reglas de autoinserción hacen lo siguiente:

const dameUnColor = () => {
    return;
    {
        color: 'white';
    }
};
console.log(dameUnColor().color);

¡BAM! El código que crea el objeto a retornar es inalcanzable. Creo que está lo suficientemente claro.

Alguien podría decir, "Pero esto sólo pasa de vez en cuando. ¡’no es tan grave!’" y ese alguien dejaría claro que jamás ha trabajado en un proyecto profesional serio, y no sabe que la mayor parte del tiempo de desarrollo se lo comen los bugs que no te esperas que aparezcan casi nunca.

Pero no poner puntos y coma no solo provoca bugs de vez en cuando, saltándose la regla 3. También se salta las reglas 1 y 2, porque hace el código más difícil de leer en general.

Y digo esto, porque como ahora todos usamos Babel para transpilar el código ES6, alguien podría añadir al transpilador un paso que corrigiera la autoinserción y evitara estos bugs.

Escribir Javascript sin puntos y coma es como escribir castellano sin signos de puntuación. Está bien para el whatsapp (discutible), pero si quieres que el lector entienda lo que quieres decir se lo estás poniendo más difícil.

Sobra decir que el lector puedes ser tú mismo una semana después. Y todo programador que se precie sabe que dos semanas después no te acuerdas ni de que iba el programa que estás leyendo.

Escribe código que se auto-explique cuanto antes

No, no he cambiado de tema. Tiene que ver con las reglas 1 y 2 y es el principal motivo por el que no debes usar const para definir funciones y no debes usar funciones flecha por regla general.

Si usas funciones flecha tendrás que definirlas como variable o como constante. Por cierto hablar de funciones como constantes suena a contrasentido para mi, confuso, como mínimo, matemáticamente hablando, pero no quiero cambiar de tema, perdón.

Y si declaras funciones con var, let o const vas a tener que declararlas antes de usarlas, lo cual significa que la secuencia de llamadas a funciones que deja claro que es lo que hace tu programa, suponiendo que les has puesto nombres útiles y descriptivos no estará al principio de tu programa.

Por eso este estilo de programación:

factorial(5);

function factorial(a) {
   if (a <= 1) {
       return 1;
   }   
   return mult(a, factorial(a-1));
}

function mult (a,b) {
    return a * b;
}

En el que lo que hace el programa aparece en la primera línea, es muy superior a este otro:

const mult = (a, b) => a * b
const factorial = a => {
	if(a <=1) {
		return 1
	}
	return mult(a, factorial(a-1))
}
factorial(5)

En el que lo primero que aparecen son detalles de implementación… y continúa con los detalles de implementación hasta que sólo al final nos dice que es lo que queremos hacer con el programa.

En general, creo que las funciones flecha tienen su utilidad para funciones cortas de una línea, que pasas como callback de un solo uso. No es poca utilidad, yo las uso bastante, pero no para sustituir completamente la declaración formal de funciones.

Acerca del uso de const y let tengo mucho más que decir, pero este artículo ya es bastante largo así que lo dejaré para otro día con un: "Bien usados están si se hace semánticamente, es decir, si usas let para definir una variable que sólo debe ser accesible dentro de un bloque más reducido que la propia función, o usas const para definir algo que tenga sentido que sea constante".

Escribir código legible y útil para los demás y para ti mismo, no va de usar menos caracteres, ni usar los patrones que están más de moda. Va de tener unas reglas y hábitos que hagan que la intención de tu código sea clara desde el primer momento, no haya ambigüedades y esté lo más lejos de los bugs que sea posible.

Por supuesto, eres libre de estar en desacuerdo y expresarlo en los comentarios, me encantaría saber de tus razones si ese es el caso.

2 Comments

  1. Hola, muy bueno el árticulo y muy de acuerdo con lo que comentas.

    Sólo añadiría que el hecho de declarar funciones como variables o constantes, así que como la idea en si de utilizar funciones flecha esta ligada al ámbito de la programación funcional.

    Quiero decir, si declaras una función flecha en una variable, es porque tienes la intención de aplicarla en una función de orden superior como puede ser map, filter, reduce… si lo que necesitas es declarar una función que varias partes de tu código necesita, es decir, buscas reutilizar y no repetir código usa «function» como bien dices.

    El hecho de usar «const» al asignar una función entiendo que esta ligado a que esa variable (constante) es una expresión que representa un comportamiento y si le has asignado «la función multiplicación» no te gustaría que más tarde otro o tu mismo le asignará otro comportamiento.

    Sobre los «;» NO SE PUEDE ESTAR MAS DE ACUERDO, uno como yo que viene del mundo Java los «;» los ignora no los ve y a la vez los necesita. Como cuando lees, no te fijas en las «comas» o los «puntos» no molestan, un caracter más no hace daño. Pero los necesitas igual que uno necesita las comas para respirar, igual el interprete o el compilador.

    Saludos

    Responder

    1. La verdad es que a día de hoy me he «rendido» con estos temas en algunos de mis proyectos.

      Al final cuando trabajas en equipo o bien tienes que llegar a un acuerdo para mantener el código coherente o heredas código de otros y no vas a ser tú el pintas que escribe todo en un estilo diferente para fastidiar al resto de personal.
      Por encima de todo esto está, para mi, el principio de «mínima sorpresa» al leer tu código. Si estás en un contexto en el que todo está escrito con const y de repente de encuentras un var te vas a pasar un rato dandole vueltas a por qué en ese caso el que lo escribió decidió hacer algo diferente.

      Respecto a lo que comentas de las funciones flecha. Para mi, y considerando primero lo anterior, las funciones declaradas con function siempre tendrán la ventaja de que puedes ponerlas donde quieras dentro del mismo archivo, en vez de estar obligado a declararlas antes si usas const. Y con más razón porque normalmente cuando las vas a usar en un map, filter, etc, son un detalle de implementación que yo prefiero no leer cuando estoy pensando en la lógica a alto nivel, y por tanto yo pondría al final del archivo.

      Si puedo elegir, yo rara vez uso const para declarar funciones, …quizás si estoy haciendo un módulo separado con un montón de funciones cortitas que se usan en muchos sitios.

      Responder

Deja una respuesta

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

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.